Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Lesson module external functions tests
 *
 * @package    mod_lesson
 * @category   external
 * @copyright  2017 Juan Leyva <juan@moodle.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 * @since      Moodle 3.3
 */

namespace mod_lesson\external;

use externallib_advanced_testcase;
use mod_lesson_external;
use lesson;
> use core_external\external_api; > use core_external\external_settings;
defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot . '/webservice/tests/helpers.php'); require_once($CFG->dirroot . '/mod/lesson/locallib.php'); /** * Silly class to access mod_lesson_external internal methods. * * @package mod_lesson * @copyright 2017 Juan Leyva <juan@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 3.3 */ class testable_mod_lesson_external extends mod_lesson_external { /** * Validates a new attempt. * * @param lesson $lesson lesson instance * @param array $params request parameters * @param boolean $return whether to return the errors or throw exceptions * @return [array the errors (if return set to true) * @since Moodle 3.3 */ public static function validate_attempt(lesson $lesson, $params, $return = false) { return parent::validate_attempt($lesson, $params, $return); } } /** * Lesson module external functions tests * * @package mod_lesson * @category external * @copyright 2017 Juan Leyva <juan@moodle.com> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @since Moodle 3.3 */ class external_test extends externallib_advanced_testcase {
> /** @var \stdClass course record. */ /** > protected \stdClass $course; * Set up for every test > */ > /** @var \stdClass */ public function setUp(): void { > protected \stdClass $lesson; global $DB; > $this->resetAfterTest(); > /** @var \stdClass a fieldset object, false or exception if error not found. */ $this->setAdminUser(); > protected \stdClass $page1; > // Setup test data. > /** @var \stdClass a fieldset object false or exception if error not found. */ $this->course = $this->getDataGenerator()->create_course(); > protected $page2; $this->lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $this->course->id)); > $lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); > /** @var \core\context\module context instance. */ $this->page1 = $lessongenerator->create_content($this->lesson); > protected \core\context\module $context; $this->page2 = $lessongenerator->create_question_truefalse($this->lesson); > $this->context = \context_module::instance($this->lesson->cmid); > /** @var \stdClass */ $this->cm = get_coursemodule_from_instance('lesson', $this->lesson->id); > protected \stdClass $cm; > // Create users. > /** @var \stdClass user record. */ $this->student = self::getDataGenerator()->create_user(); > protected \stdClass $student; $this->teacher = self::getDataGenerator()->create_user(); > > /** @var \stdClass user record. */ // Users enrolments. > protected \stdClass $teacher; $this->studentrole = $DB->get_record('role', array('shortname' => 'student')); > $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); > /** @var \stdClass a fieldset object, false or exception if error not found. */ $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual'); > protected \stdClass $studentrole; $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual'); > } > /** @var \stdClass a fieldset object, false or exception if error not found. */ > protected \stdClass $teacherrole; >
/** * Test test_mod_lesson_get_lessons_by_courses */ public function test_mod_lesson_get_lessons_by_courses() {
< global $DB, $CFG; < require_once($CFG->libdir . '/externallib.php');
> global $DB;
// Create additional course. $course2 = self::getDataGenerator()->create_course(); // Second lesson. $record = new \stdClass(); $record->course = $course2->id; $record->name = '<span lang="en" class="multilang">English</span><span lang="es" class="multilang">EspaƱol</span>'; $lesson2 = self::getDataGenerator()->create_module('lesson', $record); // Execute real Moodle enrolment as we'll call unenrol() method on the instance later. $enrol = enrol_get_plugin('manual'); $enrolinstances = enrol_get_instances($course2->id, true); foreach ($enrolinstances as $courseenrolinstance) { if ($courseenrolinstance->enrol == "manual") { $instance2 = $courseenrolinstance; break; } } $enrol->enrol_user($instance2, $this->student->id, $this->studentrole->id); self::setUser($this->student); // Enable multilang filter to on content and heading. \filter_manager::reset_caches(); filter_set_global_state('multilang', TEXTFILTER_ON); filter_set_applies_to_strings('multilang', true); // Set WS filtering.
< $wssettings = \external_settings::get_instance();
> $wssettings = external_settings::get_instance();
$wssettings->set_filter(true); $returndescription = mod_lesson_external::get_lessons_by_courses_returns(); // Create what we expect to be returned when querying the two courses. // First for the student user.
< $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles', 'practice', < 'modattempts', 'usepassword', 'grade', 'custom', 'ongoing', 'usemaxgrade',
> $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles', 'lang', > 'practice', 'modattempts', 'usepassword', 'grade', 'custom', 'ongoing', 'usemaxgrade',
'maxanswers', 'maxattempts', 'review', 'nextpagedefault', 'feedback', 'minquestions', 'maxpages', 'timelimit', 'retake', 'mediafile', 'mediafiles', 'mediaheight', 'mediawidth', 'mediaclose', 'slideshow', 'width', 'height', 'bgcolor', 'displayleft', 'displayleftif', 'progressbar', 'allowofflineattempts'); // Add expected coursemodule and data. $lesson1 = $this->lesson; $lesson1->coursemodule = $lesson1->cmid; $lesson1->introformat = 1; $lesson1->introfiles = []; $lesson1->mediafiles = [];
> $lesson1->lang = '';
$lesson2->coursemodule = $lesson2->cmid; $lesson2->introformat = 1; $lesson2->introfiles = []; $lesson2->mediafiles = [];
> $lesson2->lang = '';
$booltypes = array('practice', 'modattempts', 'usepassword', 'custom', 'ongoing', 'review', 'feedback', 'retake', 'slideshow', 'displayleft', 'progressbar', 'allowofflineattempts'); foreach ($expectedfields as $field) { if (in_array($field, $booltypes)) { $lesson1->{$field} = (bool) $lesson1->{$field}; $lesson2->{$field} = (bool) $lesson2->{$field}; } $expected1[$field] = $lesson1->{$field}; $expected2[$field] = $lesson2->{$field}; } $expected2['name'] = 'English'; // Lang filtered expected. $expectedlessons = array($expected2, $expected1); // Call the external function passing course ids. $result = mod_lesson_external::get_lessons_by_courses(array($course2->id, $this->course->id));
< $result = \external_api::clean_returnvalue($returndescription, $result);
> $result = external_api::clean_returnvalue($returndescription, $result);
$this->assertEquals($expectedlessons, $result['lessons']); $this->assertCount(0, $result['warnings']); // Call the external function without passing course id. $result = mod_lesson_external::get_lessons_by_courses();
< $result = \external_api::clean_returnvalue($returndescription, $result);
> $result = external_api::clean_returnvalue($returndescription, $result);
$this->assertEquals($expectedlessons, $result['lessons']); $this->assertCount(0, $result['warnings']); // Unenrol user from second course and alter expected lessons. $enrol->unenrol_user($instance2, $this->student->id); array_shift($expectedlessons); // Call the external function without passing course id. $result = mod_lesson_external::get_lessons_by_courses();
< $result = \external_api::clean_returnvalue($returndescription, $result);
> $result = external_api::clean_returnvalue($returndescription, $result);
$this->assertEquals($expectedlessons, $result['lessons']); // Call for the second course we unenrolled the user from, expected warning. $result = mod_lesson_external::get_lessons_by_courses(array($course2->id)); $this->assertCount(1, $result['warnings']); $this->assertEquals('1', $result['warnings'][0]['warningcode']); $this->assertEquals($course2->id, $result['warnings'][0]['itemid']); // Now, try as a teacher for getting all the additional fields. self::setUser($this->teacher); $additionalfields = array('password', 'dependency', 'conditions', 'activitylink', 'available', 'deadline', 'timemodified', 'completionendreached', 'completiontimespent'); foreach ($additionalfields as $field) { $expectedlessons[0][$field] = $lesson1->{$field}; } $result = mod_lesson_external::get_lessons_by_courses();
< $result = \external_api::clean_returnvalue($returndescription, $result);
> $result = external_api::clean_returnvalue($returndescription, $result);
$this->assertEquals($expectedlessons, $result['lessons']); // Admin also should get all the information. self::setAdminUser(); $result = mod_lesson_external::get_lessons_by_courses(array($this->course->id));
< $result = \external_api::clean_returnvalue($returndescription, $result);
> $result = external_api::clean_returnvalue($returndescription, $result);
$this->assertEquals($expectedlessons, $result['lessons']); // Now, add a restriction. $this->setUser($this->student); $DB->set_field('lesson', 'usepassword', 1, array('id' => $lesson1->id)); $DB->set_field('lesson', 'password', 'abc', array('id' => $lesson1->id)); $lessons = mod_lesson_external::get_lessons_by_courses(array($this->course->id));
< $lessons = \external_api::clean_returnvalue(mod_lesson_external::get_lessons_by_courses_returns(), $lessons);
> $lessons = external_api::clean_returnvalue(mod_lesson_external::get_lessons_by_courses_returns(), $lessons);
$this->assertFalse(isset($lessons['lessons'][0]['intro'])); } /** * Test the validate_attempt function. */ public function test_validate_attempt() { global $DB; $this->setUser($this->student); // Test deadline. $oldtime = time() - DAYSECS; $DB->set_field('lesson', 'deadline', $oldtime, array('id' => $this->lesson->id)); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('lessonclosed', key($validation)); $this->assertCount(1, $validation); // Test not available yet. $futuretime = time() + DAYSECS; $DB->set_field('lesson', 'deadline', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'available', $futuretime, array('id' => $this->lesson->id)); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('lessonopen', key($validation)); $this->assertCount(1, $validation); // Test password. $DB->set_field('lesson', 'deadline', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'available', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'usepassword', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'password', 'abc', array('id' => $this->lesson->id)); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('passwordprotectedlesson', key($validation)); $this->assertCount(1, $validation); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => 'abc'], true); $this->assertCount(0, $validation); // Dependencies. $record = new \stdClass(); $record->course = $this->course->id; $lesson2 = self::getDataGenerator()->create_module('lesson', $record); $DB->set_field('lesson', 'usepassword', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'password', '', array('id' => $this->lesson->id)); $DB->set_field('lesson', 'dependency', $lesson->id, array('id' => $this->lesson->id)); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $lesson->conditions = serialize((object) ['completed' => true, 'timespent' => 0, 'gradebetterthan' => 0]); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('completethefollowingconditions', key($validation)); $this->assertCount(1, $validation); // Lesson withou pages. $lesson = new lesson($lesson2); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('lessonnotready2', key($validation)); $this->assertCount(1, $validation); // Test retakes. $DB->set_field('lesson', 'dependency', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'retake', 0, array('id' => $this->lesson->id)); $record = [ 'lessonid' => $this->lesson->id, 'userid' => $this->student->id, 'grade' => 100, 'late' => 0, 'completed' => 1, ]; $DB->insert_record('lesson_grades', (object) $record); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => ''], true); $this->assertEquals('noretake', key($validation)); $this->assertCount(1, $validation); // Test time limit restriction. $timenow = time(); // Create a timer for the current user. $timer1 = new \stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 0; $timer1->starttime = $timenow - DAYSECS; $timer1->lessontime = $timenow; $timer1->id = $DB->insert_record("lesson_timer", $timer1); // Out of time. $DB->set_field('lesson', 'timelimit', HOURSECS, array('id' => $this->lesson->id)); $lesson = new lesson($DB->get_record('lesson', array('id' => $this->lesson->id))); $validation = testable_mod_lesson_external::validate_attempt($lesson, ['password' => '', 'pageid' => 1], true); $this->assertEquals('eolstudentoutoftime', key($validation)); $this->assertCount(1, $validation); } /** * Test the get_lesson_access_information function. */ public function test_get_lesson_access_information() { global $DB; $this->setUser($this->student); // Add previous attempt. $record = [ 'lessonid' => $this->lesson->id, 'userid' => $this->student->id, 'grade' => 100, 'late' => 0, 'completed' => 1, ]; $DB->insert_record('lesson_grades', (object) $record); $result = mod_lesson_external::get_lesson_access_information($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_lesson_access_information_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_access_information_returns(), $result);
$this->assertFalse($result['canmanage']); $this->assertFalse($result['cangrade']); $this->assertFalse($result['canviewreports']); $this->assertFalse($result['leftduringtimedsession']); $this->assertEquals(1, $result['reviewmode']); $this->assertEquals(1, $result['attemptscount']); $this->assertEquals(0, $result['lastpageseen']); $this->assertEquals($this->page2->id, $result['firstpageid']); $this->assertCount(1, $result['preventaccessreasons']); $this->assertEquals('noretake', $result['preventaccessreasons'][0]['reason']); $this->assertEquals(null, $result['preventaccessreasons'][0]['data']); $this->assertEquals(get_string('noretake', 'lesson'), $result['preventaccessreasons'][0]['message']); // Now check permissions as admin. $this->setAdminUser(); $result = mod_lesson_external::get_lesson_access_information($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_lesson_access_information_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_access_information_returns(), $result);
$this->assertTrue($result['canmanage']); $this->assertTrue($result['cangrade']); $this->assertTrue($result['canviewreports']); } /** * Test test_view_lesson invalid id. */ public function test_view_lesson_invalid_id() { $this->expectException('moodle_exception'); mod_lesson_external::view_lesson(0); } /** * Test test_view_lesson user not enrolled. */ public function test_view_lesson_user_not_enrolled() { // Test not-enrolled user. $usernotenrolled = self::getDataGenerator()->create_user(); $this->setUser($usernotenrolled); $this->expectException('moodle_exception'); mod_lesson_external::view_lesson($this->lesson->id); } /** * Test test_view_lesson user student. */ public function test_view_lesson_user_student() { // Test user with full capabilities. $this->setUser($this->student); // Trigger and capture the event. $sink = $this->redirectEvents(); $result = mod_lesson_external::view_lesson($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::view_lesson_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::view_lesson_returns(), $result);
$this->assertTrue($result['status']); $events = $sink->get_events(); $this->assertCount(1, $events); $event = array_shift($events); // Checking that the event contains the expected values. $this->assertInstanceOf('\mod_lesson\event\course_module_viewed', $event); $this->assertEquals($this->context, $event->get_context()); $moodlelesson = new \moodle_url('/mod/lesson/view.php', array('id' => $this->cm->id)); $this->assertEquals($moodlelesson, $event->get_url()); $this->assertEventContextNotUsed($event); $this->assertNotEmpty($event->get_name()); } /** * Test test_view_lesson user missing capabilities. */ public function test_view_lesson_user_missing_capabilities() { // Test user with no capabilities. // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles. assign_capability('mod/lesson:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id); // Empty all the caches that may be affected by this change. accesslib_clear_all_caches_for_unit_testing(); \course_modinfo::clear_instance_cache(); $this->setUser($this->student); $this->expectException('moodle_exception'); mod_lesson_external::view_lesson($this->lesson->id); } /** * Test for get_questions_attempts */ public function test_get_questions_attempts() { global $DB; $this->setUser($this->student); $attemptnumber = 1; // Test lesson without page attempts. $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['attempts']); // Create a fake attempt for the first possible answer. $p2answers = $DB->get_records('lesson_answers', array('lessonid' => $this->lesson->id, 'pageid' => $this->page2->id), 'id'); $answerid = reset($p2answers)->id; $newpageattempt = [ 'lessonid' => $this->lesson->id, 'pageid' => $this->page2->id, 'userid' => $this->student->id, 'answerid' => $answerid, 'retry' => $attemptnumber, 'correct' => 1, 'useranswer' => '1', 'timeseen' => time(), ]; $DB->insert_record('lesson_attempts', (object) $newpageattempt); $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(1, $result['attempts']); $newpageattempt['id'] = $result['attempts'][0]['id']; $this->assertEquals($newpageattempt, $result['attempts'][0]); // Test filtering. Only correct. $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, true);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(1, $result['attempts']); // Test filtering. Only correct only for page 2. $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, true, $this->page2->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(1, $result['attempts']); // Teacher retrieve student page attempts. $this->setUser($this->teacher); $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, false, null, $this->student->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_questions_attempts_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(1, $result['attempts']); // Test exception. $this->setUser($this->student); $this->expectException('moodle_exception'); $result = mod_lesson_external::get_questions_attempts($this->lesson->id, $attemptnumber, false, null, $this->teacher->id); } /** * Test get user grade. */ public function test_get_user_grade() { global $DB; // Add grades for the user. $newgrade = [ 'lessonid' => $this->lesson->id, 'userid' => $this->student->id, 'grade' => 50, 'late' => 0, 'completed' => time(), ]; $DB->insert_record('lesson_grades', (object) $newgrade); $newgrade = [ 'lessonid' => $this->lesson->id, 'userid' => $this->student->id, 'grade' => 100, 'late' => 0, 'completed' => time(), ]; $DB->insert_record('lesson_grades', (object) $newgrade); $this->setUser($this->student); // Test lesson without multiple attemps. The first result must be returned. $result = mod_lesson_external::get_user_grade($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertEquals(50, $result['grade']); $this->assertEquals('50.00', $result['formattedgrade']); // With retakes. By default average. $DB->set_field('lesson', 'retake', 1, array('id' => $this->lesson->id)); $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->student->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertEquals(75, $result['grade']); $this->assertEquals('75.00', $result['formattedgrade']); // With retakes. With max grade setting. $DB->set_field('lesson', 'usemaxgrade', 1, array('id' => $this->lesson->id)); $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->student->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertEquals(100, $result['grade']); $this->assertEquals('100.00', $result['formattedgrade']); // Test as teacher we get the same result. $this->setUser($this->teacher); $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->student->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_user_grade_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertEquals(100, $result['grade']); $this->assertEquals('100.00', $result['formattedgrade']); // Test exception. As student try to retrieve grades from teacher. $this->setUser($this->student); $this->expectException('moodle_exception'); $result = mod_lesson_external::get_user_grade($this->lesson->id, $this->teacher->id); } /** * Test get_user_attempt_grade */ public function test_get_user_attempt_grade() { global $DB; // Create a fake attempt for the first possible answer. $attemptnumber = 1; $p2answers = $DB->get_records('lesson_answers', array('lessonid' => $this->lesson->id, 'pageid' => $this->page2->id), 'id'); $answerid = reset($p2answers)->id; $newpageattempt = [ 'lessonid' => $this->lesson->id, 'pageid' => $this->page2->id, 'userid' => $this->student->id, 'answerid' => $answerid, 'retry' => $attemptnumber, 'correct' => 1, 'useranswer' => '1', 'timeseen' => time(), ]; $DB->insert_record('lesson_attempts', (object) $newpageattempt); // Test first without custom scoring. All questions receive the same value if correctly responsed. $DB->set_field('lesson', 'custom', 0, array('id' => $this->lesson->id)); $this->setUser($this->student); $result = mod_lesson_external::get_user_attempt_grade($this->lesson->id, $attemptnumber, $this->student->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_grade_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_grade_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertEquals(1, $result['grade']['nquestions']); $this->assertEquals(1, $result['grade']['attempts']); $this->assertEquals(1, $result['grade']['total']); $this->assertEquals(1, $result['grade']['earned']); $this->assertEquals(100, $result['grade']['grade']); $this->assertEquals(0, $result['grade']['nmanual']); $this->assertEquals(0, $result['grade']['manualpoints']); // With custom scoring, in this case, we don't retrieve any values since we are using questions without particular score. $DB->set_field('lesson', 'custom', 1, array('id' => $this->lesson->id)); $result = mod_lesson_external::get_user_attempt_grade($this->lesson->id, $attemptnumber, $this->student->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_grade_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_grade_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertEquals(1, $result['grade']['nquestions']); $this->assertEquals(1, $result['grade']['attempts']); $this->assertEquals(0, $result['grade']['total']); $this->assertEquals(0, $result['grade']['earned']); $this->assertEquals(0, $result['grade']['grade']); $this->assertEquals(0, $result['grade']['nmanual']); $this->assertEquals(0, $result['grade']['manualpoints']); } /** * Test get_content_pages_viewed */ public function test_get_content_pages_viewed() { global $DB; // Create another content pages. $lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); $page3 = $lessongenerator->create_content($this->lesson); $branch1 = new \stdClass; $branch1->lessonid = $this->lesson->id; $branch1->userid = $this->student->id; $branch1->pageid = $this->page1->id; $branch1->retry = 1; $branch1->flag = 0; $branch1->timeseen = time(); $branch1->nextpageid = $page3->id; $branch1->id = $DB->insert_record("lesson_branch", $branch1); $branch2 = new \stdClass; $branch2->lessonid = $this->lesson->id; $branch2->userid = $this->student->id; $branch2->pageid = $page3->id; $branch2->retry = 1; $branch2->flag = 0; $branch2->timeseen = time() + 1; $branch2->nextpageid = 0; $branch2->id = $DB->insert_record("lesson_branch", $branch2); // Test first attempt. $result = mod_lesson_external::get_content_pages_viewed($this->lesson->id, 1, $this->student->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_content_pages_viewed_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_content_pages_viewed_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(2, $result['pages']); foreach ($result['pages'] as $page) { if ($page['id'] == $branch1->id) { $this->assertEquals($branch1, (object) $page); } else { $this->assertEquals($branch2, (object) $page); } } // Attempt without pages viewed. $result = mod_lesson_external::get_content_pages_viewed($this->lesson->id, 3, $this->student->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_content_pages_viewed_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_content_pages_viewed_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['pages']); } /** * Test get_user_timers */ public function test_get_user_timers() { global $DB; // Create a couple of timers for the current user. $timer1 = new \stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 1; $timer1->starttime = time() - WEEKSECS; $timer1->lessontime = time(); $timer1->timemodifiedoffline = time(); $timer1->id = $DB->insert_record("lesson_timer", $timer1); $timer2 = new \stdClass; $timer2->lessonid = $this->lesson->id; $timer2->userid = $this->student->id; $timer2->completed = 0; $timer2->starttime = time() - DAYSECS; $timer2->lessontime = time() + 1; $timer2->timemodifiedoffline = time() + 1; $timer2->id = $DB->insert_record("lesson_timer", $timer2); // Test retrieve timers. $result = mod_lesson_external::get_user_timers($this->lesson->id, $this->student->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_user_timers_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_user_timers_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(2, $result['timers']); foreach ($result['timers'] as $timer) { if ($timer['id'] == $timer1->id) { $this->assertEquals($timer1, (object) $timer); } else { $this->assertEquals($timer2, (object) $timer); } } } /** * Test for get_pages */ public function test_get_pages() { global $DB; $this->setAdminUser(); // Create another content page. $lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson'); $page3 = $lessongenerator->create_content($this->lesson); $p2answers = $DB->get_records('lesson_answers', array('lessonid' => $this->lesson->id, 'pageid' => $this->page2->id), 'id'); // Add files everywhere. $fs = get_file_storage(); $filerecord = array( 'contextid' => $this->context->id, 'component' => 'mod_lesson', 'filearea' => 'page_contents', 'itemid' => $this->page1->id, 'filepath' => '/', 'filename' => 'file.txt', 'sortorder' => 1 ); $fs->create_file_from_string($filerecord, 'Test resource file'); $filerecord['itemid'] = $page3->id; $fs->create_file_from_string($filerecord, 'Test resource file'); foreach ($p2answers as $answer) { $filerecord['filearea'] = 'page_answers'; $filerecord['itemid'] = $answer->id; $fs->create_file_from_string($filerecord, 'Test resource file'); $filerecord['filearea'] = 'page_responses'; $fs->create_file_from_string($filerecord, 'Test resource file'); } $result = mod_lesson_external::get_pages($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_pages_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_pages_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(3, $result['pages']); // Check pages and values. foreach ($result['pages'] as $page) { if ($page['page']['id'] == $this->page2->id) { $this->assertEquals(2 * count($page['answerids']), $page['filescount']); $this->assertEquals('Lesson TF question 2', $page['page']['title']); } else { // Content page, no answers. $this->assertCount(0, $page['answerids']); $this->assertEquals(1, $page['filescount']); } } // Now, as student without pages menu. $this->setUser($this->student); $DB->set_field('lesson', 'displayleft', 0, array('id' => $this->lesson->id)); $result = mod_lesson_external::get_pages($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_pages_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_pages_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(3, $result['pages']); foreach ($result['pages'] as $page) { $this->assertArrayNotHasKey('title', $page['page']); } } /** * Test launch_attempt. Time restrictions already tested in test_validate_attempt. */ public function test_launch_attempt() { global $DB, $SESSION; // Test time limit restriction. $timenow = time(); // Create a timer for the current user. $timer1 = new \stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 0; $timer1->starttime = $timenow; $timer1->lessontime = $timenow; $timer1->id = $DB->insert_record("lesson_timer", $timer1); $DB->set_field('lesson', 'timelimit', 30, array('id' => $this->lesson->id)); unset($SESSION->lesson_messages); $result = mod_lesson_external::launch_attempt($this->lesson->id, '', 1);
< $result = \external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(2, $result['messages']); $messages = []; foreach ($result['messages'] as $message) { $messages[] = $message['type']; } sort($messages); $this->assertEquals(['center', 'notifyproblem'], $messages); } /** * Test launch_attempt not finished forcing review mode. */ public function test_launch_attempt_not_finished_in_review_mode() { global $DB, $SESSION; // Create a timer for the current user. $timenow = time(); $timer1 = new \stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 0; $timer1->starttime = $timenow; $timer1->lessontime = $timenow; $timer1->id = $DB->insert_record("lesson_timer", $timer1); unset($SESSION->lesson_messages); $this->setUser($this->teacher); $result = mod_lesson_external::launch_attempt($this->lesson->id, '', 1, true);
< $result = \external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result);
// Everything ok as teacher. $this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['messages']); // Should fails as student. $this->setUser($this->student); // Now, try to review this attempt. We should not be able because is a non-finished attempt. $this->expectException('moodle_exception'); mod_lesson_external::launch_attempt($this->lesson->id, '', 1, true); } /** * Test launch_attempt just finished forcing review mode. */ public function test_launch_attempt_just_finished_in_review_mode() { global $DB, $SESSION, $USER; // Create a timer for the current user. $timenow = time(); $timer1 = new \stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 1; $timer1->starttime = $timenow; $timer1->lessontime = $timenow; $timer1->id = $DB->insert_record("lesson_timer", $timer1); // Create attempt. $newpageattempt = [ 'lessonid' => $this->lesson->id, 'pageid' => $this->page2->id, 'userid' => $this->student->id, 'answerid' => 0, 'retry' => 0, // First attempt is always 0. 'correct' => 1, 'useranswer' => '1', 'timeseen' => time(), ]; $DB->insert_record('lesson_attempts', (object) $newpageattempt); // Create grade. $record = [ 'lessonid' => $this->lesson->id, 'userid' => $this->student->id, 'grade' => 100, 'late' => 0, 'completed' => 1, ]; $DB->insert_record('lesson_grades', (object) $record); unset($SESSION->lesson_messages); $this->setUser($this->student); $result = mod_lesson_external::launch_attempt($this->lesson->id, '', $this->page2->id, true);
< $result = \external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result);
// Everything ok as student. $this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['messages']); } /** * Test launch_attempt not just finished forcing review mode. */ public function test_launch_attempt_not_just_finished_in_review_mode() { global $DB, $CFG, $SESSION; // Create a timer for the current user. $timenow = time(); $timer1 = new \stdClass; $timer1->lessonid = $this->lesson->id; $timer1->userid = $this->student->id; $timer1->completed = 1; $timer1->starttime = $timenow - DAYSECS; $timer1->lessontime = $timenow - $CFG->sessiontimeout - HOURSECS; $timer1->id = $DB->insert_record("lesson_timer", $timer1); unset($SESSION->lesson_messages); // Everything ok as teacher. $this->setUser($this->teacher); $result = mod_lesson_external::launch_attempt($this->lesson->id, '', 1, true);
< $result = \external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::launch_attempt_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['messages']); // Fail as student. $this->setUser($this->student); $this->expectException('moodle_exception'); mod_lesson_external::launch_attempt($this->lesson->id, '', 1, true); } /* * Test get_page_data */ public function test_get_page_data() { global $DB; // Test a content page first (page1). $result = mod_lesson_external::get_page_data($this->lesson->id, $this->page1->id, '', false, true);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_page_data_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_page_data_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(0, $result['answers']); // No answers, auto-generated content page. $this->assertEmpty($result['ongoingscore']); $this->assertEmpty($result['progress']); $this->assertEquals($this->page1->id, $result['newpageid']); // No answers, so is pointing to the itself. $this->assertEquals($this->page1->id, $result['page']['id']); $this->assertEquals(0, $result['page']['nextpageid']); // Is the last page. $this->assertEquals('Content', $result['page']['typestring']); $this->assertEquals($this->page2->id, $result['page']['prevpageid']); // Previous page. // Check contents. $this->assertTrue(strpos($result['pagecontent'], $this->page1->title) !== false); $this->assertTrue(strpos($result['pagecontent'], $this->page1->contents) !== false); // Check menu availability. $this->assertFalse($result['displaymenu']); // Check now a page with answers (true / false) and with menu available. $DB->set_field('lesson', 'displayleft', 1, array('id' => $this->lesson->id)); $result = mod_lesson_external::get_page_data($this->lesson->id, $this->page2->id, '', false, true);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_page_data_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_page_data_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(2, $result['answers']); // One for true, one for false. // Check menu availability. $this->assertTrue($result['displaymenu']); // Check contents. $this->assertTrue(strpos($result['pagecontent'], $this->page2->contents) !== false); $this->assertEquals(0, $result['page']['prevpageid']); // Previous page. $this->assertEquals($this->page1->id, $result['page']['nextpageid']); // Next page. } /** * Test get_page_data as student */ public function test_get_page_data_student() { // Now check using a normal student account. $this->setUser($this->student); // First we need to launch the lesson so the timer is on. mod_lesson_external::launch_attempt($this->lesson->id); $result = mod_lesson_external::get_page_data($this->lesson->id, $this->page2->id, '', false, true);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_page_data_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_page_data_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(2, $result['answers']); // One for true, one for false. // Check contents. $this->assertTrue(strpos($result['pagecontent'], $this->page2->contents) !== false); // Check we don't see answer information. $this->assertArrayNotHasKey('jumpto', $result['answers'][0]); $this->assertArrayNotHasKey('score', $result['answers'][0]); $this->assertArrayNotHasKey('jumpto', $result['answers'][1]); $this->assertArrayNotHasKey('score', $result['answers'][1]); } /** * Test get_page_data without launching attempt. */ public function test_get_page_data_without_launch() { // Now check using a normal student account. $this->setUser($this->student); $this->expectException('moodle_exception'); $result = mod_lesson_external::get_page_data($this->lesson->id, $this->page2->id, '', false, true); } /** * Creates an attempt for the given userwith a correct or incorrect answer and optionally finishes it. * * @param \stdClass $user Create an attempt for this user * @param boolean $correct If the answer should be correct * @param boolean $finished If we should finish the attempt * @return array the result of the attempt creation or finalisation */ protected function create_attempt($user, $correct = true, $finished = false) { global $DB; $this->setUser($user); // First we need to launch the lesson so the timer is on. mod_lesson_external::launch_attempt($this->lesson->id); $DB->set_field('lesson', 'feedback', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'progressbar', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'custom', 0, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'maxattempts', 3, array('id' => $this->lesson->id)); $answercorrect = 0; $answerincorrect = 0; $p2answers = $DB->get_records('lesson_answers', array('lessonid' => $this->lesson->id, 'pageid' => $this->page2->id), 'id'); foreach ($p2answers as $answer) { if ($answer->jumpto == 0) { $answerincorrect = $answer->id; } else { $answercorrect = $answer->id; } } $data = array( array( 'name' => 'answerid', 'value' => $correct ? $answercorrect : $answerincorrect, ), array( 'name' => '_qf__lesson_display_answer_form_truefalse', 'value' => 1, ) ); $result = mod_lesson_external::process_page($this->lesson->id, $this->page2->id, $data);
< $result = \external_api::clean_returnvalue(mod_lesson_external::process_page_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::process_page_returns(), $result);
if ($finished) { $result = mod_lesson_external::finish_attempt($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::finish_attempt_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::finish_attempt_returns(), $result);
} return $result; } /** * Test process_page */ public function test_process_page() { global $DB; // Attempt first with incorrect response. $result = $this->create_attempt($this->student, false, false); $this->assertEquals($this->page2->id, $result['newpageid']); // Same page, since the answer was incorrect. $this->assertFalse($result['correctanswer']); // Incorrect answer. $this->assertEquals(50, $result['progress']); // Attempt with correct response. $result = $this->create_attempt($this->student, true, false); $this->assertEquals($this->page1->id, $result['newpageid']); // Next page, the answer was correct. $this->assertTrue($result['correctanswer']); // Correct response. $this->assertFalse($result['maxattemptsreached']); // Still one attempt. $this->assertEquals(50, $result['progress']); } /** * Test finish attempt not doing anything. */ public function test_finish_attempt_not_doing_anything() { $this->setUser($this->student); // First we need to launch the lesson so the timer is on. mod_lesson_external::launch_attempt($this->lesson->id); $result = mod_lesson_external::finish_attempt($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::finish_attempt_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::finish_attempt_returns(), $result);
$this->assertCount(0, $result['warnings']); $returneddata = []; foreach ($result['data'] as $data) { $returneddata[$data['name']] = $data['value']; } $this->assertEquals(1, $returneddata['gradelesson']); // Graded lesson. $this->assertEquals(1, $returneddata['welldone']); // Finished correctly (even without grades). $gradeinfo = json_decode($returneddata['gradeinfo']); $expectedgradeinfo = (object) [ 'nquestions' => 0, 'attempts' => 0, 'total' => 0, 'earned' => 0, 'grade' => 0, 'nmanual' => 0, 'manualpoints' => 0, ]; } /** * Test finish attempt with correct answer. */ public function test_finish_attempt_with_correct_answer() { // Create a finished attempt. $result = $this->create_attempt($this->student, true, true); $this->assertCount(0, $result['warnings']); $returneddata = []; foreach ($result['data'] as $data) { $returneddata[$data['name']] = $data['value']; } $this->assertEquals(1, $returneddata['gradelesson']); // Graded lesson. $this->assertEquals(1, $returneddata['numberofpagesviewed']); $this->assertEquals(1, $returneddata['numberofcorrectanswers']); $gradeinfo = json_decode($returneddata['gradeinfo']); $expectedgradeinfo = (object) [ 'nquestions' => 1, 'attempts' => 1, 'total' => 1, 'earned' => 1, 'grade' => 100, 'nmanual' => 0, 'manualpoints' => 0, ]; } /** * Test get_attempts_overview */ public function test_get_attempts_overview() { global $DB; // Create a finished attempt with incorrect answer. $this->setCurrentTimeStart(); $this->create_attempt($this->student, false, true); $this->setAdminUser(); $result = mod_lesson_external::get_attempts_overview($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result);
// One attempt, 0 for grade (incorrect response) in overal statistics. $this->assertEquals(1, $result['data']['numofattempts']); $this->assertEquals(0, $result['data']['avescore']); $this->assertEquals(0, $result['data']['highscore']); $this->assertEquals(0, $result['data']['lowscore']); // Check one student, finished attempt, 0 for grade. $this->assertCount(1, $result['data']['students']); $this->assertEquals($this->student->id, $result['data']['students'][0]['id']); $this->assertEquals(0, $result['data']['students'][0]['bestgrade']); $this->assertCount(1, $result['data']['students'][0]['attempts']); $this->assertEquals(1, $result['data']['students'][0]['attempts'][0]['end']); $this->assertEquals(0, $result['data']['students'][0]['attempts'][0]['grade']); $this->assertTimeCurrent($result['data']['students'][0]['attempts'][0]['timestart']); $this->assertTimeCurrent($result['data']['students'][0]['attempts'][0]['timeend']); // Add a new attempt (same user). sleep(1); // Allow first retake. $DB->set_field('lesson', 'retake', 1, array('id' => $this->lesson->id)); // Create a finished attempt with correct answer. $this->setCurrentTimeStart(); $this->create_attempt($this->student, true, true); $this->setAdminUser(); $result = mod_lesson_external::get_attempts_overview($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result);
// Two attempts with maximum grade. $this->assertEquals(2, $result['data']['numofattempts']); $this->assertEquals(50.00, format_float($result['data']['avescore'], 2)); $this->assertEquals(100, $result['data']['highscore']); $this->assertEquals(0, $result['data']['lowscore']); // Check one student, finished two attempts, 100 for final grade. $this->assertCount(1, $result['data']['students']); $this->assertEquals($this->student->id, $result['data']['students'][0]['id']); $this->assertEquals(100, $result['data']['students'][0]['bestgrade']); $this->assertCount(2, $result['data']['students'][0]['attempts']); foreach ($result['data']['students'][0]['attempts'] as $attempt) { if ($attempt['try'] == 0) { // First attempt, 0 for grade. $this->assertEquals(0, $attempt['grade']); } else { $this->assertEquals(100, $attempt['grade']); } } // Now, add other user failed attempt. $student2 = self::getDataGenerator()->create_user(); $this->getDataGenerator()->enrol_user($student2->id, $this->course->id, $this->studentrole->id, 'manual'); $this->create_attempt($student2, false, true); // Now check we have two students and the statistics changed. $this->setAdminUser(); $result = mod_lesson_external::get_attempts_overview($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result);
// Total of 3 attempts with maximum grade. $this->assertEquals(3, $result['data']['numofattempts']); $this->assertEquals(33.33, format_float($result['data']['avescore'], 2)); $this->assertEquals(100, $result['data']['highscore']); $this->assertEquals(0, $result['data']['lowscore']); // Check students. $this->assertCount(2, $result['data']['students']); } /** * Test get_attempts_overview when there aren't attempts. */ public function test_get_attempts_overview_no_attempts() { $this->setAdminUser(); $result = mod_lesson_external::get_attempts_overview($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_attempts_overview_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertArrayNotHasKey('data', $result); } /** * Test get_user_attempt */ public function test_get_user_attempt() { global $DB; // Create a finished and unfinished attempt with incorrect answer. $this->setCurrentTimeStart(); $this->create_attempt($this->student, true, true); $DB->set_field('lesson', 'retake', 1, array('id' => $this->lesson->id)); sleep(1); $this->create_attempt($this->student, false, false); $this->setAdminUser(); // Test first attempt finished. $result = mod_lesson_external::get_user_attempt($this->lesson->id, $this->student->id, 0);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_returns(), $result);
$this->assertCount(2, $result['answerpages']); // 2 pages in the lesson. $this->assertCount(2, $result['answerpages'][0]['answerdata']['answers']); // 2 possible answers in true/false. $this->assertEquals(100, $result['userstats']['grade']); // Correct answer. $this->assertEquals(1, $result['userstats']['gradeinfo']['total']); // Total correct answers. $this->assertEquals(100, $result['userstats']['gradeinfo']['grade']); // Correct answer. // Check page object contains the lesson pages answered. $pagesanswered = array(); foreach ($result['answerpages'] as $answerp) { $pagesanswered[] = $answerp['page']['id']; } sort($pagesanswered); $this->assertEquals(array($this->page1->id, $this->page2->id), $pagesanswered); // Test second attempt unfinished. $result = mod_lesson_external::get_user_attempt($this->lesson->id, $this->student->id, 1);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_returns(), $result);
$this->assertCount(2, $result['answerpages']); // 2 pages in the lesson. $this->assertCount(2, $result['answerpages'][0]['answerdata']['answers']); // 2 possible answers in true/false. $this->assertArrayNotHasKey('gradeinfo', $result['userstats']); // No grade info since it not finished. // Check as student I can get this information for only me. $this->setUser($this->student); // Test first attempt finished. $result = mod_lesson_external::get_user_attempt($this->lesson->id, $this->student->id, 0);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_user_attempt_returns(), $result);
$this->assertCount(2, $result['answerpages']); // 2 pages in the lesson. $this->assertCount(2, $result['answerpages'][0]['answerdata']['answers']); // 2 possible answers in true/false. $this->assertEquals(100, $result['userstats']['grade']); // Correct answer. $this->assertEquals(1, $result['userstats']['gradeinfo']['total']); // Total correct answers. $this->assertEquals(100, $result['userstats']['gradeinfo']['grade']); // Correct answer. $this->expectException('moodle_exception'); $result = mod_lesson_external::get_user_attempt($this->lesson->id, $this->teacher->id, 0); } /** * Test get_pages_possible_jumps */ public function test_get_pages_possible_jumps() { $this->setAdminUser(); $result = mod_lesson_external::get_pages_possible_jumps($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_pages_possible_jumps_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_pages_possible_jumps_returns(), $result);
$this->assertCount(0, $result['warnings']); $this->assertCount(3, $result['jumps']); // 3 jumps, 2 from the question page and 1 from the content. foreach ($result['jumps'] as $jump) { if ($jump['answerid'] != 0) { // Check only pages with answers. if ($jump['jumpto'] == 0) { $this->assertEquals($jump['pageid'], $jump['calculatedjump']); // 0 means to jump to current page. } else { // Question is configured to jump to next page if correct. $this->assertEquals($this->page1->id, $jump['calculatedjump']); } } } } /** * Test get_pages_possible_jumps when offline attemps are disabled for a normal user */ public function test_get_pages_possible_jumps_with_offlineattemps_disabled() { $this->setUser($this->student->id); $result = mod_lesson_external::get_pages_possible_jumps($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_pages_possible_jumps_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_pages_possible_jumps_returns(), $result);
$this->assertCount(0, $result['jumps']); } /** * Test get_pages_possible_jumps when offline attemps are enabled for a normal user */ public function test_get_pages_possible_jumps_with_offlineattemps_enabled() { global $DB; $DB->set_field('lesson', 'allowofflineattempts', 1, array('id' => $this->lesson->id)); $this->setUser($this->student->id); $result = mod_lesson_external::get_pages_possible_jumps($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_pages_possible_jumps_returns(), $result);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_pages_possible_jumps_returns(), $result);
$this->assertCount(3, $result['jumps']); } /* * Test get_lesson user student. */ public function test_get_lesson_user_student() { // Test user with full capabilities. $this->setUser($this->student); // Lesson not using password. $result = mod_lesson_external::get_lesson($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); < $this->assertCount(36, $result['lesson']); // Expect most of the fields.
> $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); > $this->assertCount(37, $result['lesson']); // Expect most of the fields.
$this->assertFalse(isset($result['password'])); } /** * Test get_lesson user student with missing password. */ public function test_get_lesson_user_student_with_missing_password() { global $DB; // Test user with full capabilities. $this->setUser($this->student); $DB->set_field('lesson', 'usepassword', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'password', 'abc', array('id' => $this->lesson->id)); // Lesson not using password. $result = mod_lesson_external::get_lesson($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); < $this->assertCount(6, $result['lesson']); // Expect just this few fields.
> $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); > $this->assertCount(7, $result['lesson']); // Expect just this few fields.
$this->assertFalse(isset($result['intro'])); } /** * Test get_lesson user student with correct password. */ public function test_get_lesson_user_student_with_correct_password() { global $DB; // Test user with full capabilities. $this->setUser($this->student); $password = 'abc'; $DB->set_field('lesson', 'usepassword', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'password', $password, array('id' => $this->lesson->id)); // Lesson not using password. $result = mod_lesson_external::get_lesson($this->lesson->id, $password);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); < $this->assertCount(36, $result['lesson']);
> $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); > $this->assertCount(37 , $result['lesson']);
$this->assertFalse(isset($result['intro'])); } /** * Test get_lesson teacher. */ public function test_get_lesson_teacher() { global $DB; // Test user with full capabilities. $this->setUser($this->teacher); $password = 'abc'; $DB->set_field('lesson', 'usepassword', 1, array('id' => $this->lesson->id)); $DB->set_field('lesson', 'password', $password, array('id' => $this->lesson->id)); // Lesson not passing a valid password (but we are teachers, we should see all the info). $result = mod_lesson_external::get_lesson($this->lesson->id);
< $result = \external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); < $this->assertCount(45, $result['lesson']); // Expect all the fields.
> $result = external_api::clean_returnvalue(mod_lesson_external::get_lesson_returns(), $result); > $this->assertCount(46, $result['lesson']); // Expect all the fields.
$this->assertEquals($result['lesson']['password'], $password); } }