AutorÃa | Ultima modificación | Ver Log |
<?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/>.
/**
* General unit tests for block_completion_progress.
*
* @package block_completion_progress
* @copyright 2017 onwards Nelson Moller {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace block_completion_progress;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/assign/locallib.php');
require_once($CFG->dirroot.'/blocks/moodleblock.class.php');
require_once($CFG->dirroot.'/blocks/completion_progress/block_completion_progress.php');
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
use block_completion_progress\completion_progress;
use block_completion_progress\defaults;
/**
* General unit tests for block_completion_progress.
*
* @package block_completion_progress
* @copyright 2017 onwards Nelson Moller {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class general_test extends \advanced_testcase {
/**
* Teacher users.
* @var array
*/
private $teachers = [];
/**
* Student users.
* @var array
*/
private $students = [];
/**
* Number of students to create.
*/
const STUDENT_COUNT = 4;
/**
* Create a course and add enrol users to it.
*/
protected function setUp(): void {
$this->resetAfterTest(true);
set_config('enablecompletion', 1);
$generator = $this->getDataGenerator();
$this->course = $generator->create_course([
'enablecompletion' => 1,
]);
$this->teachers[0] = $generator->create_and_enrol($this->course, 'teacher');
for ($i = 0; $i < self::STUDENT_COUNT; $i++) {
$status = $i >= 3 ? ENROL_USER_SUSPENDED : null;
$this->students[$i] = $generator->create_and_enrol($this->course, 'student',
null, 'manual', 0, 0, $status);
}
}
/**
* Convenience function to create a testable instance of an assignment.
*
* @param array $params Array of parameters to pass to the generator
* @return assign Assign class.
*/
protected function create_assign_instance($params) {
$generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
$params['course'] = $this->course->id;
$instance = $generator->create_instance($params);
$cm = get_coursemodule_from_instance('assign', $instance->id);
$context = \context_module::instance($cm->id);
return new \assign($context, $cm, $this->course);
}
/**
* Check that a student's excluded grade hides the activity from the student's progress bar.
* @covers \block_completion_progress\completion_progress
*/
public function test_grade_excluded() {
global $DB, $PAGE;
$output = $PAGE->get_renderer('block_completion_progress');
// Add a block.
$context = \context_course::instance($this->course->id);
$blockinfo = [
'parentcontextid' => $context->id,
'pagetypepattern' => 'course-view-*',
'showinsubcontexts' => 0,
'defaultweight' => 5,
'timecreated' => time(),
'timemodified' => time(),
'defaultregion' => 'side-post',
'configdata' => base64_encode(serialize((object)[
'orderby' => defaults::ORDERBY,
'longbars' => defaults::LONGBARS,
'progressBarIcons' => defaults::PROGRESSBARICONS,
'showpercentage' => defaults::SHOWPERCENTAGE,
'progressTitle' => "",
'activitiesincluded' => defaults::ACTIVITIESINCLUDED,
])),
];
$blockinstance = $this->getDataGenerator()->create_block('completion_progress', $blockinfo);
$assign = $this->create_assign_instance([
'submissiondrafts' => 0,
'completionsubmit' => 1,
'completion' => COMPLETION_TRACKING_AUTOMATIC
]);
$gradeitem = \grade_item::fetch(['courseid' => $this->course->id,
'itemtype' => 'mod', 'itemmodule' => 'assign',
'iteminstance' => $assign->get_course_module()->instance]);
// Set student 1's grade to be excluded.
$grade = $gradeitem->get_grade($this->students[1]->id);
$grade->set_excluded(1);
// Student 0 ought to see the activity.
$progress = (new completion_progress($this->course))
->for_user($this->students[0])
->for_block_instance($blockinstance);
$this->assertEquals(
[$assign->get_course_module()->id => COMPLETION_INCOMPLETE],
$progress->get_completions()
);
// Student 1 ought not see the activity.
$progress = (new completion_progress($this->course))
->for_user($this->students[1])
->for_block_instance($blockinstance);
$this->assertEquals([], $progress->get_completions());
}
/**
* Test checking of pages at site-level or not.
* @covers \block_completion_progress
*/
public function test_on_site_page() {
global $PAGE;
$generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
$instance = $generator->create_instance(['course' => $this->course->id]);
$cm = get_coursemodule_from_instance('assign', $instance->id);
// Front page.
$page = new \moodle_page();
$page->set_pagetype('site-index');
$page->set_context(\context_course::instance(SITEID));
$this->assertTrue(\block_completion_progress::on_site_page($page), 'front page');
// Dashboard.
$page = new \moodle_page();
$page->set_pagetype('my-index');
$page->set_context(\context_user::instance(get_admin()->id));
$this->assertTrue(\block_completion_progress::on_site_page($page), 'dashboard');
// Course.
$page = new \moodle_page();
$page->set_pagetype('course-view-topics');
$page->set_context(\context_course::instance($this->course->id));
$this->assertFalse(\block_completion_progress::on_site_page($page), 'course');
// Activity, possible by making a course block viewable on all page types.
$page = new \moodle_page();
$page->set_pagetype('mod-assign-grader');
$page->set_context(\context_module::instance($cm->id));
$this->assertFalse(\block_completion_progress::on_site_page($page), 'activity');
// AJAX-loaded fragment within a course module context.
$page = new \moodle_page();
$page->set_pagetype('site-index');
$page->set_context(\context_module::instance($cm->id));
$this->assertFalse(\block_completion_progress::on_site_page($page), 'ajax');
// An uninitialised page. This has a default system context.
$page = new \moodle_page();
$this->assertTrue(\block_completion_progress::on_site_page($page), 'uninitialised');
// Something very unusual.
$PAGE = null;
$this->assertFalse(\block_completion_progress::on_site_page(null), 'oddity');
}
/**
* Test that asynchronous course copy preserves all expected block instances.
* @covers \restore_completion_progress_block_task
*/
public function test_course_copy() {
global $DB;
$this->setAdminUser();
$context = \context_course::instance($this->course->id);
$generator = $this->getDataGenerator();
$group = $generator->create_group(['courseid' => $this->course->id, 'idnumber' => 'g1']);
$block1data = [
'parentcontextid' => $context->id,
'pagetypepattern' => 'course-view-*',
'showinsubcontexts' => 0,
'defaultweight' => 5,
'timecreated' => time(),
'timemodified' => time(),
'defaultregion' => 'side-post',
'configdata' => base64_encode(serialize((object)[
'orderby' => defaults::ORDERBY,
'longbars' => defaults::LONGBARS,
'progressBarIcons' => 0, // Non-default.
'showpercentage' => defaults::SHOWPERCENTAGE,
'progressTitle' => "Instance 1",
'activitiesincluded' => defaults::ACTIVITIESINCLUDED,
'group' => 'group-' . $group->id,
])),
];
$generator->create_block('completion_progress', $block1data);
$block2data = [
'parentcontextid' => $context->id,
'pagetypepattern' => 'course-view-*',
'showinsubcontexts' => 0,
'defaultweight' => 5,
'timecreated' => time(),
'timemodified' => time(),
'defaultregion' => 'side-post',
'configdata' => base64_encode(serialize((object)[
'orderby' => defaults::ORDERBY,
'longbars' => defaults::LONGBARS,
'progressBarIcons' => 0, // Non-default.
'showpercentage' => defaults::SHOWPERCENTAGE,
'progressTitle' => "Instance 2",
'activitiesincluded' => defaults::ACTIVITIESINCLUDED,
])),
];
$generator->create_block('completion_progress', $block2data);
$mdata = new \stdClass;
$mdata->courseid = $this->course->id;
$mdata->fullname = $this->course->fullname . ' Copy';
$mdata->shortname = $this->course->shortname . ' Copy';
$mdata->category = $this->course->category;
$mdata->visible = 1;
$mdata->startdate = $this->course->startdate;
$mdata->enddate = $this->course->enddate;
$mdata->idnumber = $this->course->idnumber . '_copy';
$mdata->userdata = 0;
if (method_exists('\copy_helper', 'process_formdata')) {
// Moodle 3.11 or higher.
$copydata = \copy_helper::process_formdata($mdata);
\copy_helper::create_copy($copydata);
} else {
// Moodle 3.10 or older.
$backupcopy = new \core_backup\copy\copy($mdata);
$backupcopy->create_copy();
}
$now = time();
$task = \core\task\manager::get_next_adhoc_task($now);
$this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task);
$this->expectOutputRegex("/Course copy/");
$task->execute();
\core\task\manager::adhoc_task_complete($task);
$copy = $DB->get_record('course', ['idnumber' => $mdata->idnumber]);
$context = \context_course::instance($copy->id);
$copygroup = groups_get_group_by_idnumber($copy->id, 'g1');
$blocks = $DB->get_records('block_instances', ['blockname' => 'completion_progress',
'parentcontextid' => $context->id]);
$this->assertCount(2, $blocks);
array_walk($blocks, function ($record) {
$record->config = unserialize(base64_decode($record->configdata));
});
$copyblockmap = array_flip(array_map(function ($record) {
return $record->config->progressTitle;
}, $blocks));
// Ensure both block instances were copied.
$this->assertArrayHasKey('Instance 1', $copyblockmap);
$this->assertArrayHasKey('Instance 2', $copyblockmap);
// Ensure the configured group got remapped by the copy.
$this->assertEquals('group-' . $copygroup->id, $blocks[$copyblockmap['Instance 1']]->config->group);
}
/**
* Test course modules view urls.
* @covers \block_completion_progress\completion_progress
*/
public function test_view_urls() {
global $DB, $PAGE;
$output = $PAGE->get_renderer('block_completion_progress');
// Add a block.
$context = \context_course::instance($this->course->id);
$blockinstance = $this->getDataGenerator()->create_block('completion_progress', [
'parentcontextid' => $context->id,
'pagetypepattern' => 'course-view-*',
'showinsubcontexts' => 0,
'defaultweight' => 5,
'timecreated' => time(),
'timemodified' => time(),
'defaultregion' => 'side-post',
'configdata' => base64_encode(serialize((object)[
'orderby' => defaults::ORDERBY,
'longbars' => defaults::LONGBARS,
'progressBarIcons' => defaults::PROGRESSBARICONS,
'showpercentage' => defaults::SHOWPERCENTAGE,
'progressTitle' => "",
'activitiesincluded' => defaults::ACTIVITIESINCLUDED,
])),
]);
$pageinstance = $this->getDataGenerator()->create_module('page', [
'course' => $this->course->id,
'completion' => COMPLETION_TRACKING_MANUAL
]);
$labelinstance = $this->getDataGenerator()->create_module('label', [
'course' => $this->course->id,
'completion' => COMPLETION_TRACKING_MANUAL
]);
$modinfo = get_fast_modinfo($this->course);
$pagecm = $modinfo->get_cm($pageinstance->cmid);
$progress = (new completion_progress($this->course))
->for_user($this->students[0])
->for_block_instance($blockinstance);
$activities = $progress->get_activities();
$this->assertEquals($pagecm->url->out(), $activities[0]->url);
$this->assertEquals('', $activities[1]->url);
}
}