Proyectos de Subversion Moodle

Rev

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);
    }
}