first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,186 @@
<?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/>.
namespace mod_lesson\backup;
use core_external\external_api;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . "/phpunit/classes/restore_date_testcase.php");
/**
* Restore date tests.
*
* @package mod_lesson
* @copyright 2017 onwards Ankit Agarwal <ankit.agrr@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_date_test extends \restore_date_testcase {
/**
* Creates an attempt for the given userwith a correct or incorrect answer and optionally finishes it.
*
* TODO This api can be better extracted to a generator.
*
* @param \stdClass $lesson Lesson object.
* @param \stdClass $page page object.
* @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($lesson, $page, $correct = true, $finished = false) {
global $DB, $USER;
// First we need to launch the lesson so the timer is on.
\mod_lesson_external::launch_attempt($lesson->id);
$DB->set_field('lesson', 'feedback', 1, array('id' => $lesson->id));
$DB->set_field('lesson', 'progressbar', 1, array('id' => $lesson->id));
$DB->set_field('lesson', 'custom', 0, array('id' => $lesson->id));
$DB->set_field('lesson', 'maxattempts', 3, array('id' => $lesson->id));
$answercorrect = 0;
$answerincorrect = 0;
$p2answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page->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($lesson->id, $page->id, $data);
$result = external_api::clean_returnvalue(\mod_lesson_external::process_page_returns(), $result);
// Create attempt.
$newpageattempt = [
'lessonid' => $lesson->id,
'pageid' => $page->id,
'userid' => $USER->id,
'answerid' => $answercorrect,
'retry' => 1, // First attempt is always 0.
'correct' => 1,
'useranswer' => '1',
'timeseen' => time(),
];
$DB->insert_record('lesson_attempts', (object) $newpageattempt);
if ($finished) {
$result = \mod_lesson_external::finish_attempt($lesson->id);
$result = external_api::clean_returnvalue(\mod_lesson_external::finish_attempt_returns(), $result);
}
return $result;
}
/**
* Test restore dates.
*/
public function test_restore_dates(): void {
global $DB, $USER;
// Create lesson data.
$record = ['available' => 100, 'deadline' => 100, 'timemodified' => 100];
list($course, $lesson) = $this->create_course_and_module('lesson', $record);
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$page = $lessongenerator->create_content($lesson);
$page2 = $lessongenerator->create_question_truefalse($lesson);
$this->create_attempt($lesson, $page2, true, true);
$timer = $DB->get_record('lesson_timer', ['lessonid' => $lesson->id]);
// Lesson grade.
$timestamp = 100;
$grade = new \stdClass();
$grade->lessonid = $lesson->id;
$grade->userid = $USER->id;
$grade->grade = 8.9;
$grade->completed = $timestamp;
$grade->id = $DB->insert_record('lesson_grades', $grade);
// User override.
$override = (object)[
'lessonid' => $lesson->id,
'groupid' => 0,
'userid' => $USER->id,
'sortorder' => 1,
'available' => 100,
'deadline' => 200
];
$DB->insert_record('lesson_overrides', $override);
// Set time fields to a constant for easy validation.
$DB->set_field('lesson_pages', 'timecreated', $timestamp);
$DB->set_field('lesson_pages', 'timemodified', $timestamp);
$DB->set_field('lesson_answers', 'timecreated', $timestamp);
$DB->set_field('lesson_answers', 'timemodified', $timestamp);
$DB->set_field('lesson_attempts', 'timeseen', $timestamp);
// Do backup and restore.
$newcourseid = $this->backup_and_restore($course);
$newlesson = $DB->get_record('lesson', ['course' => $newcourseid]);
$this->assertFieldsNotRolledForward($lesson, $newlesson, ['timemodified']);
$props = ['available', 'deadline'];
$this->assertFieldsRolledForward($lesson, $newlesson, $props);
$newpages = $DB->get_records('lesson_pages', ['lessonid' => $newlesson->id]);
$newanswers = $DB->get_records('lesson_answers', ['lessonid' => $newlesson->id]);
$newgrade = $DB->get_record('lesson_grades', ['lessonid' => $newlesson->id]);
$newoverride = $DB->get_record('lesson_overrides', ['lessonid' => $newlesson->id]);
$newtimer = $DB->get_record('lesson_timer', ['lessonid' => $newlesson->id]);
$newattempt = $DB->get_record('lesson_attempts', ['lessonid' => $newlesson->id]);
// Page time checks.
foreach ($newpages as $newpage) {
$this->assertEquals($timestamp, $newpage->timemodified);
$this->assertEquals($timestamp, $newpage->timecreated);
}
// Page answers time checks.
foreach ($newanswers as $newanswer) {
$this->assertEquals($timestamp, $newanswer->timemodified);
$this->assertEquals($timestamp, $newanswer->timecreated);
}
// Lesson override time checks.
$diff = $this->get_diff();
$this->assertEquals($override->available + $diff, $newoverride->available);
$this->assertEquals($override->deadline + $diff, $newoverride->deadline);
// Lesson grade time checks.
$this->assertEquals($timestamp, $newgrade->completed);
// Lesson timer time checks.
$this->assertEquals($timer->starttime, $newtimer->starttime);
$this->assertEquals($timer->lessontime, $newtimer->lessontime);
// Lesson attempt time check.
$this->assertEquals($timestamp, $newattempt->timeseen);
}
}
@@ -0,0 +1,98 @@
<?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/>.
namespace mod_lesson\backup;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . "/phpunit/classes/restore_date_testcase.php");
/**
* Restore override tests.
*
* @package mod_lesson
* @author 2019 Nathan Nguyen <nathannguyen@catalyst-au.net>
* @copyright Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restore_override_test extends \restore_date_testcase {
/**
* Test restore overrides.
*/
public function test_restore_overrides(): void {
global $DB, $USER;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$lessongen = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$lesson = $lessongen->create_instance(['course' => $course->id]);
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$now = 100;
$groupoverride1 = (object)[
'lessonid' => $lesson->id,
'groupid' => $group1->id,
'available' => $now,
'deadline' => $now + 20
];
$DB->insert_record('lesson_overrides', $groupoverride1);
$groupoverride2 = (object)[
'lessonid' => $lesson->id,
'groupid' => $group2->id,
'available' => $now,
'deadline' => $now + 40
];
$DB->insert_record('lesson_overrides', $groupoverride2);
// Current quiz overrides.
$overrides = $DB->get_records('lesson_overrides', ['lessonid' => $lesson->id]);
$this->assertEquals(2, count($overrides));
// User override.
$useroverride = (object)[
'lessonid' => $lesson->id,
'userid' => $USER->id,
'sortorder' => 1,
'available' => 100,
'deadline' => 200
];
$DB->insert_record('lesson_overrides', $useroverride);
// Current quiz overrides.
$overrides = $DB->get_records('lesson_overrides', ['lessonid' => $lesson->id]);
$this->assertEquals(3, count($overrides));
// Back up and restore including group info and user info.
set_config('backup_general_groups', 1, 'backup');
$newcourseid = $this->backup_and_restore($course);
$newquiz = $DB->get_record('lesson', ['course' => $newcourseid]);
$overrides = $DB->get_records('lesson_overrides', ['lessonid' => $newquiz->id]);
// 2 groups overrides and 1 user override.
$this->assertEquals(3, count($overrides));
// Back up and restore with user info and without group info.
set_config('backup_general_groups', 0, 'backup');
$newcourseid = $this->backup_and_restore($course);
$newquiz = $DB->get_record('lesson', ['course' => $newcourseid]);
$overrides = $DB->get_records('lesson_overrides', ['lessonid' => $newquiz->id]);
// 1 user override.
$this->assertEquals(1, count($overrides));
}
}
@@ -0,0 +1,91 @@
@mod @mod_lesson
Feature: Numeric and short answer questions have a section to catch all other student answers.
In order for lesson pages to catch any student answer
As a teacher
I need to fill in the sections to catch all other student answers
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
Given the following "activity" exists:
| activity | lesson |
| course | C1 |
| idnumber | 0001 |
| name | Test lesson name |
| maxattempts | 3 |
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the following fields to these values:
| Provide option to try a question again | Yes |
And I press "Save and display"
Scenario: I can create a numerical question with an option to catch all student responses.
Given I follow "Add a question page"
And I set the field "Select a question type" to "Numerical"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Numerical question |
| Page contents | What is 1 + 2? |
| id_answer_editor_0 | 3 |
| id_jumpto_0 | End of lesson |
| id_enableotheranswers | 1 |
| id_jumpto_6 | Next page |
And I press "Save page"
And I select "Add a content page" from the "qtype" singleselect
And I set the following fields to these values:
| Page title | Just move on page |
| Page contents | You are here to move on |
| id_answer_editor_0 | End this lesson |
| id_jumpto_0 | End of lesson |
And I press "Save page"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I set the field "Your answer" to "5"
And I press "Submit"
And I should see "That's the wrong answer"
And I press "Yes, I'd like to try again"
And I should see "What is 1 + 2?"
And I set the field "Your answer" to "7"
And I press "Submit"
And I should see "That's the wrong answer"
When I press "No, I just want to go on to the next question"
Then I should see "You are here to move on"
Scenario: I can create a shortanswer question with an option to catch all student responses.
Given I follow "Add a question page"
And I set the field "Select a question type" to "Short answer"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Short answer question |
| Page contents | Please type in cat |
| id_answer_editor_0 | 3 |
| id_jumpto_0 | End of lesson |
| id_enableotheranswers | 1 |
| id_jumpto_6 | Next page |
And I press "Save page"
And I select "Add a content page" from the "qtype" singleselect
And I set the following fields to these values:
| Page title | Just move on page |
| Page contents | You are here to move on |
| id_answer_editor_0 | End this lesson |
| id_jumpto_0 | End of lesson |
And I press "Save page"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I set the field "Your answer" to "dog"
And I press "Submit"
And I should see "That's the wrong answer"
And I press "Yes, I'd like to try again"
And I should see "Please type in cat"
And I set the field "Your answer" to "bird"
And I press "Submit"
And I should see "That's the wrong answer"
When I press "No, I just want to go on to the next question"
Then I should see "You are here to move on"
@@ -0,0 +1,79 @@
<?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/>.
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
// For that reason, we can't even rely on $CFG->admin being available here.
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
use Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
use Behat\Mink\Exception\ExpectationException as ExpectationException;
/**
* Step definitions related mod_lesson.
*
* @package mod_lesson
* @category test
* @copyright 2021 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_mod_lesson_behat extends behat_base {
/**
* Select the lesson edit type [Collapsed|Expanded]
*
* @Given i select edit type :edittype
*
* @param string $edittype The edit type of either Collapsed or Expanded
*/
public function i_select_edit_type(string $edittype): void {
$typestring = ($edittype == 'Collapsed') ? get_string('collapsed', 'mod_lesson') : get_string('full', 'mod_lesson');
try {
$this->execute("behat_forms::i_select_from_the_singleselect", [$typestring, 'jump']);
} catch (ElementNotFoundException $e) {
$this->execute("behat_general::click_link", [$typestring]);
}
}
/**
* Go to the lesson essay grading page.
*
* @Given i grade lesson essays
*/
public function i_grade_lesson_essays(): void {
try {
$this->execute("behat_general::i_click_on", [get_string('manualgrading', 'mod_lesson'), 'button']);
} catch (ElementNotFoundException $e) {
$this->execute("behat_general::click_link", [get_string('manualgrading', 'mod_lesson')]);
}
}
/**
* Go to the lesson edit page.
*
* @Given i edit the lesson
*/
public function i_edit_the_lesson(): void {
try {
$this->execute("behat_general::click_link", [get_string('editlesson', 'mod_lesson')]);
} catch (ElementNotFoundException $e) {
$this->execute("behat_general::i_click_on_in_the",
[get_string('editlesson', 'mod_lesson'), 'button', 'region-main', 'region']
);
}
}
}
@@ -0,0 +1,46 @@
@mod @mod_lesson @javascript
Feature: Set end of lesson reached as a completion condition for a lesson
In order to ensure students really see all lesson pages
As a teacher
I need to set end of lesson reached to mark the lesson activity as completed
Scenario: Set end reached as a condition
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category | enablecompletion |
| Course 1 | C1 | 0 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exist:
| activity | name | course | idnumber | completion | completionview | completionendreached |
| lesson | Test lesson | C1 | 0001 | 2 | 0 | 1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson | content | First page name | First page contents |
| Test lesson | content | Second page name | Second page contents |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto |
| First page name | Next page | Next page |
| Second page name | Previous page | Previous page |
| Second page name | Next page | Next page |
When I am on the "Course 1" course page logged in as student1
Then the "Go through the activity to the end" completion condition of "Test lesson" is displayed as "todo"
And I follow "Test lesson"
And I press "Next page"
And I am on "Course 1" course homepage
And the "Go through the activity to the end" completion condition of "Test lesson" is displayed as "todo"
And I am on the "Test lesson" "lesson activity" page
And I should see "You have seen more than one page of this lesson already."
And I should see "Do you want to start at the last page you saw?"
And I click on "No" "link" in the "#page-content" "css_element"
And I press "Next page"
And I press "Next page"
And I am on "Course 1" course homepage
And the "Go through the activity to the end" completion condition of "Test lesson" is displayed as "done"
And I am on the "Course 1" course page logged in as teacher1
And "Student 1" user has completed "Test lesson" activity
@@ -0,0 +1,51 @@
@mod @mod_lesson @javascript
Feature: Set time spent as a completion condition for a lesson
In order to ensure students spend the needed time to study lessons
As a teacher
I need to set time spent to mark the lesson activity as completed
Scenario: Set time spent as a condition
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category | enablecompletion |
| Course 1 | C1 | 0 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exist:
| activity | name | course | idnumber | completion | completionview | completiontimespentenabled | completiontimespent |
| lesson | Test lesson | C1 | 0001 | 2 | 0 | 1 | 5 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson | content | First page name | First page contents |
| Test lesson | content | Second page name | Second page contents |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto |
| First page name | Next page | Next page |
| Second page name | Previous page | Previous page |
| Second page name | Next page | Next page |
When I am on the "Course 1" course page logged in as student1
Then the "Spend at least 5 secs on this activity" completion condition of "Test lesson" is displayed as "todo"
And I follow "Test lesson"
And I press "Next page"
# Add 1 sec delay so lesson knows a valid attempt has been made in past.
And I wait "1" seconds
And I press "Next page"
And I should see "You completed this lesson in"
And I should see ", which is less than the required time of 5 secs. You might need to attempt the lesson again."
And I am on "Course 1" course homepage
And the "Spend at least 5 secs on this activity" completion condition of "Test lesson" is displayed as "todo"
And I am on the "Test lesson" "lesson activity" page
And I press "Next page"
And I wait "5" seconds
And I press "Next page"
And I should not see "You might need to attempt the lesson again."
And I am on "Course 1" course homepage
And the "Spend at least 5 secs on this activity" completion condition of "Test lesson" is displayed as "done"
And I am on the "Course 1" course page logged in as teacher1
And "Student 1" user has completed "Test lesson" activity
@@ -0,0 +1,55 @@
@mod @mod_lesson
Feature: A teacher can set available from and deadline dates to access a lesson
In order to schedule lesson activities
As a teacher
I need to set available from and deadline dates
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson | C1 | lesson1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson | content | First page name | First page contents |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto |
| First page name | Next page | Next page |
Scenario: Forbidding lesson accesses until a specified date
Given I am on the "Test lesson" "lesson activity editing" page logged in as teacher1
And I set the field "id_available_enabled" to "1"
And I set the following fields to these values:
| available[day] | 1 |
| available[month] | January |
| available[year] | 2030 |
| available[hour] | 08 |
| available[minute] | 00 |
And I press "Save and display"
When I am on the "Test lesson" "lesson activity" page logged in as student1
Then the activity date in "Test lesson" should contain "Opens: Tuesday, 1 January 2030, 8:00"
And I should not see "First page contents"
Scenario: Forbidding lesson accesses after a specified date
Given I am on the "Test lesson" "lesson activity editing" page logged in as teacher1
And I set the field "id_deadline_enabled" to "1"
And I set the following fields to these values:
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2000 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save and display"
When I am on the "Test lesson" "lesson activity" page logged in as student1
Then the activity date in "Test lesson" should contain "Closed: Saturday, 1 January 2000, 8:00"
And I should not see "First page contents"
@@ -0,0 +1,47 @@
@mod @mod_lesson
Feature: Display the lesson description in the lesson and optionally in the course
In order to display the the lesson description description in the course
As a teacher
I need to enable the 'Display description on course page' setting.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | format |
| Course 1 | C1 | topics |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
And the following "activity" exist:
| activity | name | intro | course | idnumber |
| lesson | Test lesson name | Test lesson description | C1 | 0001 |
And the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | content | Test lesson part 1 | Test lesson part 1 |
And the following "mod_lesson > answer" exist:
| page | answer | jumpto |
| Test lesson part 1 | Next page | Next page |
Scenario: Description is displayed in the Lesson
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
Then I should see "Test lesson description"
Scenario: Show lesson description in the course homepage
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And the following fields match these values:
| Display description on course page | |
And I set the following fields to these values:
| Display description on course page | 1 |
And I press "Save and return to course"
When I am on "Course 1" course homepage
Then I should see "Test lesson description"
Scenario: Hide lesson description in the course homepage
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And the following fields match these values:
| Display description on course page | |
And I press "Save and return to course"
When I am on "Course 1" course homepage
Then I should not see "Test lesson description"
@@ -0,0 +1,131 @@
@mod @mod_lesson @javascript @editor_tiny
Feature: In a lesson activity, a teacher can duplicate a lesson page
In order to duplicate a lesson page
As a teacher
I need to add content to a lesson
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | lesson |
| name | Test lesson name |
And the following "user private files" exist:
| user | filepath |
| teacher1 | mod/lesson/tests/fixtures/moodle_logo.jpg |
And I log in as "teacher1"
Scenario: Duplicate content page with an image.
When I am on the "Test lesson name" "lesson activity" page
And I follow "Add a content page"
And I set the following fields to these values:
| Page title | First page name |
| Page contents | First page contents |
| id_answer_editor_0 | Next page |
| id_jumpto_0 | Next page |
| id_answer_editor_1 | Previous page |
| id_jumpto_1 | Previous page |
And I click on "Image" "button" in the "Page contents" "form_row"
And I wait until "Browse repositories" "button" exists
And I click on "Browse repositories" "button"
And I click on "Private files" "link" in the ".fp-repo-area" "css_element"
And I click on "moodle_logo.jpg" "link"
And I click on "Select this file" "button"
And I set the field "How would you describe this image to someone who can't see it?" to "It's the logo"
And I click on "Save" "button" in the "Image details" "dialogue"
And I press "Save page"
And I follow "Duplicate page: First page name"
Then I should see "Inserted page: First page name"
And I follow "Update page: First page name"
And I set the field "Page title" to "Introduction page"
And I press "Save page"
And I follow "Update page: First page name"
Then I should see "First page name"
And I switch to the "Page contents" TinyMCE editor iframe
Then "//*[contains(@data-id, 'id_contents_editor')]//img[contains(@src, 'moodle_logo.jpg')]" "xpath_element" should exist
Scenario: Duplicate question page with image in answer.
When I am on the "Test lesson name" "lesson activity" page
And I follow "Add a question page"
And I set the field "Select a question type" to "True/false"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | True false with an image in the answer |
| Page contents | Select the picture |
| id_answer_editor_0 | Answer text |
| id_response_editor_0 | Correct answer |
| id_jumpto_0 | End of lesson |
| id_score_0 | 1 |
| id_answer_editor_1 | 1 |
| id_response_editor_1 | Incorrect answer |
| id_jumpto_1 | This page |
| id_score_1 | 0 |
And I click on "Image" "button" in the "//*[@id='id_answer_editor_0']/ancestor::*[@data-fieldtype='editor']" "xpath_element"
And I wait until "Browse repositories" "button" exists
And I click on "Browse repositories" "button"
And I click on "Private files" "link" in the ".fp-repo-area" "css_element"
And I click on "moodle_logo.jpg" "link"
And I click on "Select this file" "button"
And I set the field "How would you describe this image to someone who can't see it?" to "It's the logo"
And I click on "Save" "button" in the "Image details" "dialogue"
And I press "Save page"
And I follow "Duplicate page: True false with an image in the answer"
Then I should see "Inserted page: True false with an image in the answer"
And I follow "Update page: True false with an image in the answer"
And I set the field "Page title" to "First true false"
And I press "Save page"
And I follow "Update page: True false with an image in the answer"
Then I should see "True false with an image in the answer"
And I switch to the "Page contents" TinyMCE editor iframe
Then I should see "Select the picture"
And I switch to the main frame
And I switch to the "Answer" TinyMCE editor iframe
Then "//*[contains(@data-id, 'id_answer_editor_0')]//img[contains(@src, 'moodle_logo.jpg')]" "xpath_element" should exist
Scenario: Duplicate question page with image in feedback.
When I am on the "Test lesson name" "lesson activity" page
And I follow "Add a question page"
And I set the field "Select a question type" to "True/false"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | True false with an image in the feedback |
| Page contents | Select the picture |
| id_answer_editor_0 | Answer text |
| id_response_editor_0 | Correct answer |
| id_jumpto_0 | End of lesson |
| id_score_0 | 1 |
| id_answer_editor_1 | 1 |
| id_response_editor_1 | Incorrect answer |
| id_jumpto_1 | This page |
| id_score_1 | 0 |
And I click on "Image" "button" in the "//*[@id='id_response_editor_0']/ancestor::*[@data-fieldtype='editor']" "xpath_element"
And I wait until "Browse repositories" "button" exists
And I click on "Browse repositories" "button"
And I click on "Private files" "link" in the ".fp-repo-area" "css_element"
And I click on "moodle_logo.jpg" "link"
And I click on "Select this file" "button"
And I set the field "How would you describe this image to someone who can't see it?" to "It's the logo"
And I click on "Save" "button" in the "Image details" "dialogue"
And I press "Save page"
And I follow "Duplicate page: True false with an image in the feedback"
Then I should see "Inserted page: True false with an image in the feedback"
And I follow "Update page: True false with an image in the feedback"
And I set the field "Page title" to "First true false"
And I press "Save page"
And I follow "Update page: True false with an image in the feedback"
Then I should see "True false with an image in the feedback"
And I switch to the "Page contents" TinyMCE editor iframe
Then I should see "Select the picture"
And I switch to the main frame
And I switch to the "Response" TinyMCE editor iframe
Then "//*[contains(@data-id, 'id_response_editor_0')]//img[contains(@src, 'moodle_logo.jpg')]" "xpath_element" should exist
@@ -0,0 +1,38 @@
@mod @mod_lesson
Feature: In a lesson activity, teacher can import blackboard fill in the blank question
As a teacher
I need to import a fill in the blank question made in Blackboard in a lesson
@javascript @_file_upload
Scenario: Import fill in the blank question in a lesson
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | lesson |
| name | Test lesson name |
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I follow "Import questions"
And I set the field "File format" to "Blackboard"
And I upload "mod/lesson/tests/fixtures/sample_blackboard_fib_qti.dat" file to "Upload" filemanager
And I press "Import"
Then I should see "Importing 1 questions"
And I should see "Name an amphibian: __________"
And I press "Continue"
And I should not see "__________"
And I should not see "Your answer"
And I set the field "id_answer" to "frog"
And I press "Submit"
And I should see "Your answer : frog"
And I should see "A frog is an amphibian"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
@@ -0,0 +1,35 @@
@mod @mod_lesson
Feature: In a lesson activity, teacher can import embedded images in questions answers and responses
As a teacher
I need to import a question with images in answers and responses in a lesson
@javascript @_file_upload
Scenario: Import questions with images in answers and responses in a lesson
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | lesson |
| name | Test lesson name |
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I follow "Import questions"
And I set the field "File format" to "Moodle XML format"
And I upload "mod/lesson/tests/fixtures/multichoice.xml" file to "Upload" filemanager
And I press "Import"
Then I should see "Importing 1 questions"
And I should see " Listen to this greeting:"
And I should see "What language is being spoken?"
And I press "Continue"
And I should see "What language is being spoken?"
And "//audio[contains(@title, 'Listen to this greeting:')]/source[contains(@src, 'bonjour.mp3')]" "xpath_element" should exist
And "//*[contains(@class, 'answeroption')]//img[contains(@src, 'pluginfile.php')]" "xpath_element" should exist
And "//*[contains(@class, 'answeroption')]//img[contains(@src, 'flag-france.jpg')]" "xpath_element" should exist
@@ -0,0 +1,80 @@
@mod @mod_lesson @core_completion
Feature: View activity completion in the lesson activity
In order to have visibility of lesson completion requirements
As a student
I need to be able to view my lesson completion progress
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Vinnie | Student1 | student1@example.com |
| teacher1 | Darrell | Teacher1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | enablecompletion | showcompletionconditions |
| Course 1 | C1 | 1 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| teacher1 | C1 | editingteacher |
And the following "activity" exists:
| activity | lesson |
| course | C1 |
| idnumber | mh1 |
| name | Music history |
| section | 1 |
| completion | 2 |
| completionview | 1 |
| completionusegrade | 1 |
| completionendreached | 1 |
| completiontimespentenabled | 1 |
| completiontimespent | 3 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Music history | content | Music history part 1 | |
| Music history | essay | Music essay | Write a really interesting music essay |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto | score |
| Music history part 1 | The history of music part 1 | Next page | 0 |
| Music essay | | Next page | 1 |
Scenario: View automatic completion items as a teacher
When I am on the "Music history" "lesson activity" page logged in as teacher1
Then "Music history" should have the "View" completion condition
And "Music history" should have the "Spend at least 3 secs on this activity" completion condition
And "Music history" should have the "Go through the activity to the end" completion condition
And "Music history" should have the "Receive a grade" completion condition
Scenario: View automatic completion items as a student
Given I am on the "Music history" "lesson activity" page logged in as student1
And the "View" completion condition of "Music history" is displayed as "done"
And the "Spend at least 3 secs on this activity" completion condition of "Music history" is displayed as "todo"
And the "Go through the activity to the end" completion condition of "Music history" is displayed as "todo"
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
When I am on the "Music history" "lesson activity" page
And I wait "4" seconds
And I reload the page
And the "View" completion condition of "Music history" is displayed as "done"
And the "Spend at least 3 secs on this activity" completion condition of "Music history" is displayed as "done"
And the "Go through the activity to the end" completion condition of "Music history" is displayed as "todo"
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
And I press "The history of music part 1"
And I set the field "Your answer" to "Some drummers play with their sticks flipped around"
And I press "Submit"
Then the "View" completion condition of "Music history" is displayed as "done"
And the "Spend at least 3 secs on this activity" completion condition of "Music history" is displayed as "done"
And the "Go through the activity to the end" completion condition of "Music history" is displayed as "done"
And the "Receive a grade" completion condition of "Music history" is displayed as "done"
@javascript
Scenario: Use manual completion
Given I am on the "Music history" "lesson activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the field "Students must manually mark the activity as done" to "1"
And I press "Save and display"
# Teacher view.
And the manual completion button for "Music history" should be disabled
# Student view.
When I am on the "Music history" "lesson activity" page logged in as student1
Then the manual completion button of "Music history" is displayed as "Mark as done"
And I toggle the manual completion state of "Music history"
And the manual completion button of "Music history" is displayed as "Done"
@@ -0,0 +1,125 @@
@mod @mod_lesson
Feature: Teachers can review student progress on all lessons in a course by viewing the complete report
As a Teacher
I need to view the complete report for one of my students.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber | retake |
| lesson | Test lesson name | C1 | lesson1 | 1 |
Scenario: View student progress for lesson that was never attempted
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | truefalse | True/false question 1 | Paper is made from trees. |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| True/false question 1 | True | Correct | Next page | 1 |
| True/false question 1 | False | Wrong | This page | 0 |
And I log in as "teacher1"
When I am on "Course 1" course homepage
And I navigate to course participants
And I follow "Student 1"
And I follow "Complete report"
Then I should see "No attempts have been made on this lesson"
Scenario: View student progress for an incomplete lesson containing both content and question pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | truefalse | True/false question 1 | Paper is made from trees. |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Second page name | Next page | | Next page | 0 |
| True/false question 1 | True | Correct | Next page | 1 |
| True/false question 1 | False | Wrong | This page | 0 |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
Then I am on the "Course 1" course page logged in as teacher1
And I navigate to course participants
And I follow "Student 1"
And I follow "Complete report"
And I should see "Lesson has been started, but not yet completed"
And I should see "1" in the ".cell.c1" "css_element"
And I should see "0" in the ".cell.c2" "css_element"
Scenario: View student progress for a lesson containing both content and question pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | truefalse | True/false question 1 | Paper is made from trees. |
| Test lesson name | truefalse | True/false question 2 | The sky is Pink. |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Second page name | Previous page | | Previous page | 0 |
| Second page name | Next page | | Next page | 0 |
| True/false question 1 | True | Correct | Next page | 1 |
| True/false question 1 | False | Wrong | This page | 0 |
| True/false question 2 | False | Correct | Next page | 1 |
| True/false question 2 | True | Wrong | This page | 0 |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "Second page contents"
And I press "Next page"
And I should see "Paper is made from trees."
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "The sky is Pink."
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
Then I am on the "Course 1" course page logged in as teacher1
And I navigate to course participants
And I follow "Student 1"
And I follow "Complete report"
And I should see "Grade: 50.00 / 100.00"
And I should see "4" in the ".cell.c1" "css_element"
And I should see "2" in the ".cell.c2" "css_element"
And I should see "1" in the ".cell.c3" "css_element"
Scenario: View student attempts in a lesson containing only content pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto |
| First page name | Next page | Next page |
| Second page name | Previous page | Previous page |
| Second page name | End of lesson | End of lesson |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "Second page contents"
And I press "End of lesson"
Then I am on the "Course 1" course page logged in as teacher1
And I navigate to course participants
And I follow "Student 1"
And I follow "Complete report"
And I should see "Completed"
And I should see "2" in the ".cell.c1" "css_element"
And I should see "0" in the ".cell.c2" "css_element"
And I should see "0" in the ".cell.c3" "css_element"
@@ -0,0 +1,59 @@
@mod @mod_lesson @core_completion
Feature: Pass grade activity completion in the lesson activity
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Vinnie | Student1 | student1@example.com |
| student2 | Vinnie | Student2 | student2@example.com |
| student3 | Vinnie | Student3 | student3@example.com |
| teacher1 | Darrell | Teacher1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category | enablecompletion | showcompletionconditions |
| Course 1 | C1 | 0 | 1 | 1 |
And the following "course enrolments" exist:
| user | course | role |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| teacher1 | C1 | editingteacher |
And the following "activity" exists:
| activity | lesson |
| course | C1 |
| idnumber | mh1 |
| name | Music history |
| gradepass | 50 |
| completion | 2 |
| completionusegrade | 1 |
| completionpassgrade | 1 |
And the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Music history | numeric | Numerical question | What is 1 + 2? |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto | score |
| Numerical question | 3 | End of lesson | 1 |
| Numerical question | @#wronganswer#@ | Next page | 0 |
Scenario: View automatic completion items as a teacher
When I am on the "Music history" "lesson activity" page logged in as teacher1
And "Music history" should have the "Receive a grade" completion condition
And "Music history" should have the "Receive a passing grade" completion condition
Scenario: View automatic completion items as a student
Given I am on the "Music history" "lesson activity" page logged in as student1
And the "Receive a grade" completion condition of "Music history" is displayed as "todo"
And the "Receive a passing grade" completion condition of "Music history" is displayed as "todo"
When I am on the "Music history" "lesson activity" page
And I set the field "Your answer" to "3"
And I press "Submit"
And the "Receive a grade" completion condition of "Music history" is displayed as "done"
And the "Receive a passing grade" completion condition of "Music history" is displayed as "done"
And I am on the "Music history" "lesson activity" page logged in as student2
And I set the field "Your answer" to "0"
And I press "Submit"
And the "Receive a grade" completion condition of "Music history" is displayed as "done"
And the "Receive a passing grade" completion condition of "Music history" is displayed as "failed"
And I am on the "Course 1" course page logged in as teacher1
And "Vinnie Student1" user has completed "Music history" activity
And "Vinnie Student2" user has completed "Music history" activity
And "Vinnie Student3" user has not completed "Music history" activity
@@ -0,0 +1,93 @@
@mod @mod_lesson
Feature: Lesson reset
In order to reuse past lessons
As a teacher
I need to remove all previous data.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Tina | Teacher1 | teacher1@example.com |
| student1 | Sam1 | Student1 | student1@example.com |
| student2 | Sam2 | Student2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| Group 2 | C1 | G2 |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson name | C1 | lesson1 |
And the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | truefalse | True/false question 1 | Cat is an amphibian |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| True/false question 1 | False | Correct | Next page | 1 |
| True/false question 1 | True | Wrong | This page | 0 |
Scenario: Use course reset to clear all attempt data
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I navigate to "Reports" in current page administration
And I should see "Sam1 Student1"
And I am on the "Course 1" "reset" page
And I set the following fields to these values:
| Delete all lesson attempts | 1 |
And I press "Reset course"
And I press "Continue"
And I am on the "Test lesson name" "lesson activity" page
And I navigate to "Reports" in current page administration
Then I should see "No attempts have been made on this lesson"
@javascript
Scenario: Use course reset to remove user overrides.
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I navigate to "Overrides" in current page administration
And I follow "Add user override"
And I set the following fields to these values:
| Override user | Student1 |
| Re-takes allowed | 1 |
And I press "Save"
And I should see "Sam1 Student1"
And I am on the "Course 1" "reset" page
And I set the following fields to these values:
| Delete all user overrides | 1 |
And I press "Reset course"
And I press "Continue"
And I am on the "Test lesson name" "lesson activity" page
And I navigate to "Overrides" in current page administration
Then I should not see "Sam1 Student1"
Scenario: Use course reset to remove group overrides.
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
And I set the following fields to these values:
| Override group | Group 1 |
| Re-takes allowed | 1 |
And I press "Save"
And I should see "Group 1"
And I am on the "Course 1" "reset" page
And I set the following fields to these values:
| Delete all group overrides | 1 |
And I press "Reset course"
And I press "Continue"
And I am on the "Test lesson name" "lesson activity" page
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
Then I should not see "Group 1"
@@ -0,0 +1,284 @@
@mod @mod_lesson
Feature: In a lesson activity, teacher can create lesson's pages
In order to set up an existing lesson
As a teacher
I need to create pages in the lesson
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson name | C1 | lesson1 |
Scenario: Create content page
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I follow "Add a content page"
And I set the following fields to these values:
| Page title | First page name |
| Page contents | First page contents |
| id_answer_editor_0 | Forward |
| id_jumpto_0 | Next page |
| id_answer_editor_1 | Backward |
| id_jumpto_1 | Previous page |
And I press "Save page"
And I select edit type "Expanded"
Then I should see "First page name"
And I should see "First page contents"
And I should see "Forward" in the "Content 1" "table_row"
And I should see "Next page" in the "Jump 1" "table_row"
And I should see "Backward" in the "Content 2" "table_row"
And I should see "Previous page" in the "Jump 2" "table_row"
Scenario: Create essay page
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I follow "Add a question page"
And I set the field "Select a question type" to "Essay"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Music essay |
| Page contents | Write a really interesting music essay |
| Jump | End of lesson |
| Score | 1 |
And I press "Save page"
And I select edit type "Expanded"
Then I should see "Music essay"
And I should see "Write a really interesting music essay"
And I should see "End of lesson" in the "Jump 1" "table_row"
Scenario: Create matching page
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I follow "Add a question page"
And I set the field "Select a question type" to "Matching"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Geography |
| Page contents | Match each city with its country |
| id_answer_editor_0 | Correct! |
| id_jumpto_0 | End of lesson |
| id_score_0 | 2 |
| id_answer_editor_1 | Wrong! |
| id_jumpto_1 | This page |
| id_score_1 | 0 |
| id_answer_editor_2 | Barcelona |
| id_response_editor_2 | Spain |
| id_answer_editor_3 | Perth |
| id_response_editor_3 | Australia |
| id_answer_editor_4 | Tokyo |
| id_response_editor_4 | Japan |
| id_answer_editor_5 | Buenos Aires |
| id_response_editor_5 | Argentina |
| id_answer_editor_6 | Cairo |
| id_response_editor_6 | Egypt |
And I press "Save page"
And I select edit type "Expanded"
Then I should see "Geography"
And I should see "Match each city with its country"
And I should see "Correct!" in the "Correct response" "table_row"
And I should see "2" in the "Correct answer score" "table_row"
And I should see "End of lesson" in the "Correct answer jump" "table_row"
And I should see "Wrong!" in the "Wrong response" "table_row"
And I should see "0" in the "Wrong answer score" "table_row"
And I should see "This page" in the "Wrong answer jump" "table_row"
And I should see "Barcelona" in the "Answer 1" "table_row"
And I should see "Spain" in the "Matches with answer 1" "table_row"
And I should see "Perth" in the "Answer 2" "table_row"
And I should see "Australia" in the "Matches with answer 2" "table_row"
And I should see "Tokyo" in the "Answer 3" "table_row"
And I should see "Japan" in the "Matches with answer 3" "table_row"
And I should see "Buenos Aires" in the "Answer 4" "table_row"
And I should see "Argentina" in the "Matches with answer 4" "table_row"
And I should see "Cairo" in the "Answer 5" "table_row"
And I should see "Egypt" in the "Matches with answer 5" "table_row"
Scenario: Create multichoice page
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I follow "Add a question page"
And I set the field "Select a question type" to "Multichoice"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Multichoice question |
| Page contents | What animal is an amphibian? |
| id_answer_editor_0 | Frog |
| id_response_editor_0 | Correct answer |
| id_jumpto_0 | End of lesson |
| id_score_0 | 2 |
| id_answer_editor_1 | Cat |
| id_response_editor_1 | Incorrect answer |
| id_jumpto_1 | This page |
| id_score_1 | 0 |
| id_answer_editor_2 | Dog |
| id_response_editor_2 | Incorrect answer |
| id_jumpto_2 | Next page |
| id_score_2 | 0 |
And I press "Save page"
And I select edit type "Expanded"
Then I should see "Multichoice question"
And I should see "What animal is an amphibian?"
And I should see "Frog" in the "Answer 1" "table_row"
And I should see "Correct answer" in the "Response 1" "table_row"
And I should see "End of lesson" in the "//tr[contains(.,'Jump')][1]" "xpath_element"
And I should see "2" in the "//tr[contains(.,'Score')][1]" "xpath_element"
And I should see "Cat" in the "Answer 2" "table_row"
And I should see "Incorrect answer" in the "Response 2" "table_row"
And I should see "This page" in the "//tr[contains(.,'Jump')][2]" "xpath_element"
And I should see "0" in the "//tr[contains(.,'Score')][2]" "xpath_element"
And I should see "Dog" in the "Answer 3" "table_row"
And I should see "Incorrect answer" in the "Response 3" "table_row"
And I should see "Next page" in the "//tr[contains(.,'Jump')][3]" "xpath_element"
And I should see "0" in the "//tr[contains(.,'Score')][3]" "xpath_element"
Scenario: Create numerical page
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I follow "Add a question page"
And I set the field "Select a question type" to "Numerical"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Really hard question |
| Page contents | What is 1 + 2? |
| id_answer_editor_0 | 3 |
| id_response_editor_0 | Correct |
| id_jumpto_0 | End of lesson |
| id_score_0 | 2 |
| id_answer_editor_1 | 2 |
| id_response_editor_1 | Close, but wrong |
| id_jumpto_1 | Next page |
| id_score_1 | 1 |
| id_enableotheranswers | 1 |
| id_response_editor_6 | Wrong |
| id_jumpto_6 | This page |
| id_score_6 | 0 |
And I press "Save page"
And I select edit type "Expanded"
Then I should see "Really hard question"
And I should see "What is 1 + 2?"
And I should see "3" in the "Answer 1" "table_row"
And I should see "Correct" in the "Response 1" "table_row"
And I should see "End of lesson" in the "//tr[contains(.,'Jump')][1]" "xpath_element"
And I should see "2" in the "//tr[contains(.,'Score')][1]" "xpath_element"
And I should see "2" in the "Answer 2" "table_row"
And I should see "Close, but wrong" in the "Response 2" "table_row"
And I should see "Next page" in the "//tr[contains(.,'Jump')][2]" "xpath_element"
And I should see "1" in the "//tr[contains(.,'Score')][2]" "xpath_element"
And I should see "@#wronganswer#@" in the "Answer 3" "table_row"
And I should see "Wrong" in the "Response 3" "table_row"
And I should see "This page" in the "//tr[contains(.,'Jump')][3]" "xpath_element"
And I should see "0" in the "//tr[contains(.,'Score')][3]" "xpath_element"
Scenario: Create short answer page
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I follow "Add a question page"
And I set the field "Select a question type" to "Short answer"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Geography |
| Page contents | Capital of Canada |
| id_answer_editor_0 | Ottawa |
| id_response_editor_0 | Correct |
| id_jumpto_0 | End of lesson |
| id_score_0 | 2 |
| id_answer_editor_1 | Toronto |
| id_response_editor_1 | It's in Canada, but it's not the capital |
| id_jumpto_1 | Next page |
| id_score_1 | 1 |
| id_answer_editor_2 | Vancouver |
| id_response_editor_2 | It's in Canada, but it's not the capital |
| id_jumpto_2 | Next page |
| id_score_2 | 1 |
| id_enableotheranswers | 1 |
| id_response_editor_6 | Wrong |
| id_jumpto_6 | This page |
| id_score_6 | 0 |
And I press "Save page"
And I select edit type "Expanded"
Then I should see "Geography"
And I should see "Capital of Canada"
And I should see "Ottawa" in the "Answer 1" "table_row"
And I should see "Correct" in the "Response 1" "table_row"
And I should see "End of lesson" in the "//tr[contains(.,'Jump')][1]" "xpath_element"
And I should see "2" in the "//tr[contains(.,'Score')][1]" "xpath_element"
And I should see "Toronto" in the "Answer 2" "table_row"
And I should see "It's in Canada, but it's not the capital" in the "Response 2" "table_row"
And I should see "Next page" in the "//tr[contains(.,'Jump')][2]" "xpath_element"
And I should see "1" in the "//tr[contains(.,'Score')][2]" "xpath_element"
And I should see "Vancouver" in the "Answer 3" "table_row"
And I should see "It's in Canada, but it's not the capital" in the "Response 3" "table_row"
And I should see "Next page" in the "//tr[contains(.,'Jump')][3]" "xpath_element"
And I should see "1" in the "//tr[contains(.,'Score')][3]" "xpath_element"
And I should see "@#wronganswer#@" in the "Answer 4" "table_row"
And I should see "Wrong" in the "Response 4" "table_row"
And I should see "This page" in the "//tr[contains(.,'Jump')][4]" "xpath_element"
And I should see "0" in the "//tr[contains(.,'Score')][4]" "xpath_element"
Scenario: Create true/false page
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I follow "Add a question page"
And I set the field "Select a question type" to "True/false"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | True/false question |
| Page contents | Paper is made from trees. |
| id_answer_editor_0 | True |
| id_response_editor_0 | Correct |
| id_jumpto_0 | End of lesson |
| id_score_0 | 2 |
| id_answer_editor_1 | False |
| id_response_editor_1 | Wrong |
| id_jumpto_1 | This page |
| id_score_1 | 0 |
And I press "Save page"
And I select edit type "Expanded"
Then I should see "True/false question"
And I should see "Paper is made from trees."
And I should see "True" in the "Answer 1" "table_row"
And I should see "Correct" in the "Response 1" "table_row"
And I should see "End of lesson" in the "//tr[contains(.,'Jump')][1]" "xpath_element"
And I should see "2" in the "//tr[contains(.,'Score')][1]" "xpath_element"
And I should see "False" in the "Answer 2" "table_row"
And I should see "Wrong" in the "Response 2" "table_row"
And I should see "This page" in the "//tr[contains(.,'Jump')][2]" "xpath_element"
And I should see "0" in the "//tr[contains(.,'Score')][2]" "xpath_element"
Scenario: Create cluster pages
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I follow "Add a cluster"
And I select edit type "Expanded"
And I click on "Add a question page here" "link" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' addlinks ')][2]" "xpath_element"
And I set the field "Select a question type" to "Multichoice"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | question 1 |
| Page contents | Question from cluster |
| id_answer_editor_0 | Correct answer |
| id_response_editor_0 | Good |
| id_jumpto_0 | Cluster |
| id_score_0 | 1 |
| id_answer_editor_1 | Incorrect answer |
| id_response_editor_1 | Bad |
| id_jumpto_1 | This page |
| id_score_1 | 0 |
And I press "Save page"
And I click on "Add a content page" "link" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' addlinks ')][3]" "xpath_element"
And I set the following fields to these values:
| Page title | Second page name |
| Page contents | This page mark the the beginning of the subcluster it should not be seen by students |
| id_answer_editor_0 | Next page |
| id_jumpto_0 | Next page |
And I press "Save page"
And I click on "Add an end of branch" "link" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' addlinks ')][4]" "xpath_element"
And I click on "Add an end of cluster" "link" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' addlinks ')][5]" "xpath_element"
Then I should see "Cluster"
And I should see "Unseen question within a cluster" in the "//tr[contains(.,'Jump')][1]" "xpath_element"
And I should see "Question from cluster"
And I should see "This page mark the the beginning of the subcluster it should not be seen by students"
And I should see "End of branch"
And I should see "End of cluster"
@@ -0,0 +1,72 @@
@mod @mod_lesson
Feature: In a lesson activity, teacher can delete question answers and
branch table contents
In order to modify an existing lesson
As a teacher
I need to question answers and branch table contents in the lesson
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson name | C1 | lesson1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | numeric | Hardest question ever | 1 + 1? |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| First page name | Previous page | | Previous page | 0 |
| Hardest question ever | 2 | Correct answer | End of lesson | 1 |
| Hardest question ever | 1 | Incorrect answer | First page name | 0 |
And I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I press "Edit lesson"
And I select edit type "Expanded"
Scenario: Edit lesson content page
Given I click on "//th[normalize-space(.)='First page name']/descendant::a[3]" "xpath_element"
When I set the following fields to these values:
| id_answer_editor_1 | |
And I press "Save page"
And I should not see "Previous page"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
Then I should see "First page contents"
And I should not see "Previous page"
And I press "Next page"
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 2 |
And I press "Submit"
And I should see "Correct answer"
And I should not see "Incorrect answer"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 1 (out of 1)."
Scenario: Edit lesson question page
Given I click on "//th[normalize-space(.)='Hardest question ever']/descendant::a[3]" "xpath_element"
When I set the following fields to these values:
| id_answer_editor_1 | |
And I press "Save page"
And I should not see "Incorrect answer"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
Then I should see "First page contents"
And I press "Next page"
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 1 |
And I press "Submit"
And I should not see "Incorrect answer"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 0 (out of 1)."
@@ -0,0 +1,79 @@
@mod @mod_lesson
Feature: In a lesson activity, teacher can edit a cluster page
In order to modify an existing lesson and change navigation
As a teacher
I need to edit cluster pages in the lesson
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Lesson with cluster | C1 | lesson1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Lesson with cluster | content | First page name | First page contents |
| Lesson with cluster | cluster | Cluster | Cluster |
| Lesson with cluster | multichoice | Question 1 | Question from cluster |
| Lesson with cluster | multichoice | Question 2 | Question from cluster |
| Lesson with cluster | endofcluster | End of cluster | End of cluster |
| Lesson with cluster | content | Second page name | Content page after cluster |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Cluster | | | Unseen question within a cluster | 0 |
| Question 1 | Correct answer | Good | Cluster | 1 |
| Question 1 | Incorrect answer | Bad | This page | 0 |
| Question 2 | Correct answer | Good | Cluster | 1 |
| Question 2 | Incorrect answer | Bad | This page | 0 |
| End of cluster | | | Next page | 0 |
| Second page name | Next page | | Next page | 0 |
Scenario: Edit lesson cluster page
Given I am on the "Lesson with cluster" "lesson activity" page logged in as teacher1
And I press "Edit lesson"
And I select edit type "Expanded"
And I click on "//th[normalize-space(.)='Cluster']/descendant::a[3]" "xpath_element"
When I set the following fields to these values:
| Page title | Modified name |
| Page contents | Modified contents |
And I press "Save page"
Then I should see "Modified name"
And I click on "//th[normalize-space(.)='Modified name']/descendant::a[3]" "xpath_element"
And I should see "Unseen question within a cluster"
And I press "Cancel"
And I click on "//th[normalize-space(.)='End of cluster']/descendant::a[3]" "xpath_element"
And I set the following fields to these values:
| Page title | Modified end |
| Page contents | Modified end contents |
| id_jumpto_0 | Second page name |
And I press "Save page"
And I should see "Modified end"
And I am on the "Lesson with cluster" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "Question from cluster"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
And I should see "Question from cluster"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
And I should see "Content page after cluster"
And I press "Next page"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 2 (out of 2)."
@@ -0,0 +1,100 @@
@mod @mod_lesson
Feature: In a lesson activity, teacher can edit lesson's pages
In order to modify an existing lesson
As a teacher
I need to edit pages in the lesson
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson name | C1 | lesson1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | numeric | Hardest question ever | 1 + 1? |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Second page name | Previous page | | Previous page | 0 |
| Second page name | Next page | | Next page | 0 |
| Hardest question ever | 2 | Correct answer | End of lesson | 1 |
| Hardest question ever | 1 | Incorrect answer | Second page name | 0 |
And I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I press "Edit lesson"
And I select edit type "Expanded"
Scenario: Edit lesson content page
Given I click on "//th[normalize-space(.)='Second page name']/descendant::a[3]" "xpath_element"
When I set the following fields to these values:
| Page title | Modified second page |
| Page contents | Modified contents |
| id_answer_editor_0 | Forward |
| id_jumpto_0 |Next page |
| id_answer_editor_1 | Backward |
| id_jumpto_1 | Previous page |
And I press "Save page"
Then I should see "Modified second page"
And I should not see "Second page name"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "Modified contents"
And I should not see "Second page contents"
And I press "Backward"
And I should see "First page contents"
And I press "Next page"
And I should see "Modified contents"
And I press "Forward"
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 2 |
And I press "Submit"
And I should see "Correct answer"
And I should not see "Incorrect answer"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 1 (out of 1)."
Scenario: Edit lesson question page
Given I click on "//th[normalize-space(.)='Hardest question ever']/descendant::a[3]" "xpath_element"
When I set the following fields to these values:
| Page title | New hardest question |
| Page contents | 1 + 2? |
| id_answer_editor_0 | 2 |
| id_response_editor_0 | Your answer is incorrect |
| id_jumpto_0 | End of lesson |
| id_score_0 | 0 |
| id_answer_editor_1 | 3 |
| id_response_editor_1 | Your answer is correct |
| id_jumpto_1 | End of lesson |
| id_score_1 | 1 |
And I press "Save page"
Then I should see "New hardest question"
And I should not see "Hardest question ever"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "Second page contents"
And I press "Next page"
And I should see "1 + 2?"
And I should not see "1 + 1?"
And I set the following fields to these values:
| Your answer | 3 |
And I press "Submit"
And I should see "Your answer is correct"
And I should not see "Incorrect answer"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 1 (out of 1)."
@@ -0,0 +1,58 @@
@mod @mod_lesson @_file_upload
Feature: In a lesson activity, teacher can add an essay question
As a teacher
I need to add an essay question in a lesson and grade student attempts
Scenario: questions with essay question
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber | feedback |
| lesson | Test lesson name | C1 | lesson1 | 1 |
And the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | essay | Essay question | <p>Please write a story about a <b>frog</b>.</p> |
And the following "mod_lesson > answer" exist:
| page | jumpto | score |
| Essay question | Next page | 1 |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
Then I should see "Please write a story about a frog."
And I set the field "Your answer" to "<p>Once upon a time there was a little <b>green</b> frog."
And I press "Submit"
And I should see "Your answer"
And I should see "Once upon a time there was a little green frog."
And I should not see "&lt;b&gt;"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "You earned 0 out of 0 for the automatically graded questions."
And I should see "Your 1 essay question(s) will be graded and added into your final score at a later date."
And I should see "Your current grade without the essay question(s) is 0 out of 1."
And I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I grade lesson essays
And I should see "Student 1"
And I should see "Essay question"
And I follow "Essay question"
And I should see "Student 1's response"
And I should see "Once upon a time there was a little green frog."
And I set the following fields to these values:
| Your comments | <p>Well <b>done</b>.</p> |
| Essay score | 1 |
And I press "Save changes"
And I should see "Changes saved"
And I navigate to "Reports" in current page administration
And I should see "Student 1"
And I click on ".lesson-attempt-link" "css_element" in the "Student 1" "table_row"
And I should see "Essay: Essay question"
And I should see "Please write a story about a frog."
And I should see "Once upon a time there was a little green frog."
And I should see "Well done."
And I should not see "&lt;b&gt;"
@@ -0,0 +1,103 @@
@mod @mod_lesson
Feature: In a lesson activity, students can exit and re-enter the activity when it consists only of cluster pages
As a student
I need to exit and re-enter a lesson out and into clusters.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Lesson with cluster | C1 | lesson1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Lesson with cluster | content | First page name | First page contents |
| Lesson with cluster | cluster | A Cluster | A Cluster |
| Lesson with cluster | multichoice | Question 1 A Cluster | Question 1 from A cluster |
| Lesson with cluster | multichoice | Question 2 A Cluster | Question 2 from A cluster |
| Lesson with cluster | multichoice | Question 3 A Cluster | Question 3 from A cluster |
| Lesson with cluster | endofcluster | End of A cluster | End of A cluster |
| Lesson with cluster | cluster | B Cluster | B Cluster |
| Lesson with cluster | multichoice | Question 1 B Cluster | Question 1 from B cluster |
| Lesson with cluster | multichoice | Question 2 B Cluster | Question 2 from B cluster |
| Lesson with cluster | multichoice | Question 3 B Cluster | Question 3 from B cluster |
| Lesson with cluster | endofcluster | End of B cluster | End of B cluster |
| Lesson with cluster | cluster | C Cluster | C Cluster |
| Lesson with cluster | multichoice | Question 1 C Cluster | Question 1 from C cluster |
| Lesson with cluster | multichoice | Question 2 C Cluster | Question 2 from C cluster |
| Lesson with cluster | multichoice | Question 3 C Cluster | Question 3 from C cluster |
| Lesson with cluster | endofcluster | End of C cluster | End of C cluster |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| A Cluster | | | Unseen question within a cluster | 0 |
| Question 1 A Cluster | Correct answer | Good | B Cluster | 1 |
| Question 1 A Cluster | Incorrect answer | Bad | Unseen question within a cluster | 0 |
| Question 2 A Cluster | Correct answer | Good | B Cluster | 1 |
| Question 2 A Cluster | Incorrect answer | Bad | Unseen question within a cluster | 0 |
| Question 3 A Cluster | Correct answer | Good | B Cluster | 1 |
| Question 3 A Cluster | Incorrect answer | Bad | Unseen question within a cluster | 0 |
| End of A cluster | | | Next page | 0 |
| B Cluster | | | Unseen question within a cluster | 0 |
| Question 1 B Cluster | Correct answer | Good | C Cluster | 1 |
| Question 1 B Cluster | Incorrect answer | Bad | Unseen question within a cluster | 0 |
| Question 2 B Cluster | Correct answer | Good | C Cluster | 1 |
| Question 2 B Cluster | Incorrect answer | Bad | Unseen question within a cluster | 0 |
| Question 3 B Cluster | Correct answer | Good | C Cluster | 1 |
| Question 3 B Cluster | Incorrect answer | Bad | Unseen question within a cluster | 0 |
| End of B cluster | | | Next page | 0 |
| C Cluster | | | Unseen question within a cluster | 0 |
| Question 1 C Cluster | Correct answer | Good | End of lesson | 1 |
| Question 1 C Cluster | Incorrect answer | Bad | Unseen question within a cluster | 0 |
| Question 2 C Cluster | Correct answer | Good | End of lesson | 1 |
| Question 2 C Cluster | Incorrect answer | Bad | Unseen question within a cluster | 0 |
| Question 3 C Cluster | Correct answer | Good | End of lesson | 1 |
| Question 3 C Cluster | Incorrect answer | Bad | Unseen question within a cluster | 0 |
| End of C cluster | | | Next page | 0 |
Scenario: Accessing as student to a cluster only lesson
Given I am on the "Lesson with cluster" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "Correct answer"
And I set the following fields to these values:
| Incorrect answer | 1 |
And I press "Submit"
And I should see "Bad"
And I press "Continue"
And I set the following fields to these values:
| Incorrect answer | 1 |
And I press "Submit"
And I should see "Bad"
And I press "Continue"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
And I should see "Incorrect answer"
And I set the following fields to these values:
| Incorrect answer | 1 |
And I press "Submit"
And I am on "Course 1" course homepage
And I follow "Lesson with cluster"
And I should see "Do you want to start at the last page you saw?"
And I click on "No" "link" in the "#page-content" "css_element"
And I should see "First page contents"
And I press "Next page"
And I should see "Correct answer"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
Then I should see "Correct answer"
@@ -0,0 +1,384 @@
@mod @mod_lesson
Feature: Lesson group override
In order to grant a student special access to a lesson
As a teacher
I need to create an override for that user.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Tina | Teacher1 | teacher1@example.com |
| student1 | Sam1 | Student1 | student1@example.com |
| student2 | Sam2 | Student2 | student2@example.com |
| student3 | Sam3 | Student3 | student3@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| Group 2 | C1 | G2 |
Given the following "group members" exist:
| user | group |
| student1 | G1 |
| student2 | G2 |
| student3 | G1 |
And the following "activities" exist:
| activity | name | groupmode | course | idnumber |
| lesson | Test lesson name | 1 | C1 | lesson1 |
And the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | truefalse | True/false question 1 | Cat is an amphibian |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| True/false question 1 | False | Correct | Next page | 1 |
| True/false question 1 | True | Wrong | This page | 0 |
Scenario: Add, modify then delete a group override
Given I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
And I set the following fields to these values:
| Override group | Group 1 |
| id_deadline_enabled | 1 |
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2020 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save"
And I should see "Wednesday, 1 January 2020, 8:00"
Then I click on "Edit" "link" in the "region-main" "region"
And I set the following fields to these values:
| deadline[year] | 2030 |
And I press "Save"
And I should see "Tuesday, 1 January 2030, 8:00"
And I click on "Delete" "link"
And I press "Continue"
And I should not see "Group 1"
Scenario: Duplicate a user override
Given I am on the "Test lesson name" "lesson activity" page logged in as teacher1
When I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
And I set the following fields to these values:
| Override group | Group 1 |
| id_deadline_enabled | 1 |
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2020 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save"
And I should see "Wednesday, 1 January 2020, 8:00"
Then I click on "copy" "link"
And I set the following fields to these values:
| Override group | Group 2 |
| deadline[year] | 2030 |
And I press "Save"
And I should see "Tuesday, 1 January 2030, 8:00"
And I should see "Group 2"
Scenario: Allow a single group to have re-take the lesson
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Re-takes allowed | 0 |
And I press "Save and display"
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
And I set the following fields to these values:
| Override group | Group 1 |
| Re-takes allowed | 1 |
And I press "Save"
And I should see "Re-takes allowed"
Given I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I am on the "Test lesson name" "lesson activity" page
Then I should not see "You are not allowed to retake this lesson."
And I should see "Cat is an amphibian"
Given I am on the "Test lesson name" "lesson activity" page logged in as student2
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I am on the "Test lesson name" "lesson activity" page
And I should see "You are not allowed to retake this lesson."
Scenario: Allow a single group to have a different password
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Password protected lesson | Yes |
| id_password | moodle_rules |
And I press "Save and display"
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
And I set the following fields to these values:
| Override group | Group 1 |
| Password protected lesson | 12345 |
And I press "Save"
And I should see "Password protected lesson"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
Then I should see "Test lesson name is a password protected lesson"
And I should not see "Cat is an amphibian"
And I set the field "userpassword" to "moodle_rules"
And I press "Continue"
And I should see "Login failed, please try again..."
And I should see "Test lesson name is a password protected lesson"
And I set the field "userpassword" to "12345"
And I press "Continue"
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I am on the "Test lesson name" "lesson activity" page logged in as student2
And I should see "Test lesson name is a password protected lesson"
And I should not see "Cat is an amphibian"
And I set the field "userpassword" to "12345"
And I press "Continue"
And I should see "Login failed, please try again..."
And I should see "Test lesson name is a password protected lesson"
And I set the field "userpassword" to "moodle_rules"
And I press "Continue"
Scenario: Allow a group to have a different due date
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| id_deadline_enabled | 1 |
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2000 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save and display"
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
And I set the following fields to these values:
| Override group | Group 1 |
| id_deadline_enabled | 1 |
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2030 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save"
And I should see "Lesson closes"
And I am on the "Test lesson name" "lesson activity" page logged in as student2
Then the activity date in "Test lesson name" should contain "Closed: Saturday, 1 January 2000, 8:00"
And I should not see "Cat is an amphibian"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
Scenario: Allow a group to have a different start date
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| id_available_enabled | 1 |
| available[day] | 1 |
| available[month] | January |
| available[year] | 2030 |
| available[hour] | 08 |
| available[minute] | 00 |
And I press "Save and display"
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
And I set the following fields to these values:
| Override group | Group 1 |
| id_available_enabled | 1 |
| available[day] | 1 |
| available[month] | January |
| available[year] | 2015 |
| available[hour] | 08 |
| available[minute] | 00 |
And I press "Save"
And I should see "Lesson opens"
And I am on the "Test lesson name" "lesson activity" page logged in as student2
Then the activity date in "Test lesson name" should contain "Opens: Tuesday, 1 January 2030, 8:00"
And I should not see "Cat is an amphibian"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
Scenario: Allow a single group to have multiple attempts at each question
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Re-takes allowed | 1 |
And I press "Save and display"
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
And I set the following fields to these values:
| Override group | Group 1 |
| Maximum number of attempts per question | 2 |
And I press "Save"
And I should see "Maximum number of attempts per question"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I am on the "Test lesson name" "lesson activity" page logged in as student2
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
Then I press "Continue"
And I should see "Congratulations - end of lesson reached"
@javascript
Scenario: Add both a user and group override and verify that both are applied correctly
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
When I set the following fields to these values:
| id_available_enabled | 1 |
| available[day] | 1 |
| available[month] | January |
| available[year] | 2040 |
| available[hour] | 08 |
| available[minute] | 00 |
And I press "Save and display"
And I am on the "Test lesson name" "lesson activity" page
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
And I set the following fields to these values:
| Override group | Group 1 |
| id_available_enabled | 1 |
| available[day] | 1 |
| available[month] | January |
| available[year] | 2030 |
| available[hour] | 08 |
| available[minute] | 00 |
And I press "Save"
And I should see "Tuesday, 1 January 2030, 8:00"
And I am on the "Test lesson name" "lesson activity" page
And I navigate to "Overrides" in current page administration
And I follow "Add user override"
And I set the following fields to these values:
| Override user | Student1 |
| id_available_enabled | 1 |
| available[day] | 1 |
| available[month] | January |
| available[year] | 2031 |
| available[hour] | 08 |
| available[minute] | 00 |
And I press "Save"
And I should see "Wednesday, 1 January 2031, 8:00"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And the activity date in "Test lesson name" should contain "Opens: Wednesday, 1 January 2031, 8:00"
And I am on the "Test lesson name" "lesson activity" page logged in as student2
And the activity date in "Test lesson name" should contain "Opens: Sunday, 1 January 2040, 8:00"
And I am on the "Test lesson name" "lesson activity" page logged in as student3
And the activity date in "Test lesson name" should contain "Opens: Tuesday, 1 January 2030, 8:00"
Scenario: Override a group when teacher is in no group, and does not have accessallgroups permission, and the activity's group mode is 'separate groups'
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/site:accessallgroups | Prevent | editingteacher | Course | C1 |
And the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| lesson | Lesson 2 | C1 | lesson2 | 1 |
When I am on the "Lesson 2" "lesson activity" page logged in as teacher1
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
Then I should see "No groups you can access."
And I should not see "Add group override"
Scenario: A teacher without accessallgroups permission should only be able to add group override for their groups, when the activity's group mode is 'separate groups'
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/site:accessallgroups | Prevent | editingteacher | Course | C1 |
And the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| lesson | Lesson 2 | C1 | lesson2 | 1 |
And the following "group members" exist:
| user | group |
| teacher1 | G1 |
When I am on the "Lesson 2" "lesson activity" page logged in as teacher1
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
Then the "Override group" select box should contain "Group 1"
And the "Override group" select box should not contain "Group 2"
Scenario: A teacher without accessallgroups permission should only be able to see the group overrides for their groups, when the activity's group mode is 'separate groups'
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/site:accessallgroups | Prevent | editingteacher | Course | C1 |
And the following "activities" exist:
| activity | name | course | idnumber | groupmode |
| lesson | Lesson 2 | C1 | lesson2 | 1 |
And the following "group members" exist:
| user | group |
| teacher1 | G1 |
And I am on the "Lesson 2" "lesson activity" page logged in as admin
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
And I set the following fields to these values:
| Override group | Group 1 |
| id_available_enabled | 1 |
| available[day] | 1 |
| available[month] | January |
| available[year] | 2020 |
| available[hour] | 08 |
| available[minute] | 00 |
And I press "Save and enter another override"
And I set the following fields to these values:
| Override group | Group 2 |
| id_available_enabled | 1 |
| available[day] | 1 |
| available[month] | January |
| available[year] | 2020 |
| available[hour] | 08 |
| available[minute] | 00 |
And I press "Save"
When I am on the "Lesson 2" "lesson activity" page logged in as teacher1
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
Then I should see "Group 1" in the ".generaltable" "css_element"
And I should not see "Group 2" in the ".generaltable" "css_element"
Scenario: "Not visible" groups should not be available for group overrides
Given the following "groups" exist:
| name | course | idnumber | visibility | participation |
| Visible to everyone/Participation | C1 | VP | 0 | 1 |
| Only Only visible to members/Participation | C1 | MP | 1 | 1 |
| Only see own membership | C1 | O | 2 | 0 |
| Not visible | C1 | N | 3 | 0 |
| Visible to everyone/Non-Participation | C1 | VN | 0 | 0 |
| Only visible to members/Non-Participation | C1 | MN | 1 | 0 |
When I am on the "lesson1" Activity page logged in as teacher1
And I navigate to "Overrides" in current page administration
And I select "Group overrides" from the "jump" singleselect
And I follow "Add group override"
Then I should see "Visible to everyone/Participation" in the "Override group" "select"
And I should see "Visible to everyone/Non-Participation" in the "Override group" "select"
And I should see "Only visible to members" in the "Override group" "select"
And I should see "Only visible to members/Non-Participation" in the "Override group" "select"
And I should see "Only see own membership" in the "Override group" "select"
And I should not see "Not visible" in the "Override group" "select"
@@ -0,0 +1,106 @@
@mod @mod_lesson
Feature: In a lesson activity, if custom scoring is not enabled, student should see
some informations at the end of lesson: questions answered, correct answers, grade, score
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "scales" exist:
| name | scale |
| Test Scale | Disappointing, Good, Excellent |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson name | C1 | lesson1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | numeric | Hardest question ever | 1 + 1? |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Hardest question ever | 2 | Correct answer | Next page | 1 |
| Hardest question ever | 1 | Incorrect answer | This page | 0 |
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Maximum grade | 75 |
| Custom scoring | No |
And I press "Save and display"
Scenario: Informations at end of lesson if custom scoring not enabled
Given I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
When I press "Next page"
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 1 |
And I press "Submit"
And I should see "Incorrect answer"
And I press "Continue"
Then I should see "Congratulations - end of lesson reached"
And I should see "Number of questions answered: 1"
And I should see "Number of correct answers: 0"
And I should see "Your score is 0 (out of 1)."
And I should see "Your current grade is 0.0 out of 75"
Scenario: Informations at end of lesson if custom scoring not enabled with custom decimal separator
Given the following "language customisations" exist:
| component | stringid | value |
| core_langconfig | decsep | # |
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
When I press "Next page"
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 1 |
And I press "Submit"
And I should see "Incorrect answer"
And I press "Continue"
Then I should see "Congratulations - end of lesson reached"
And I should see "Number of questions answered: 1"
And I should see "Number of correct answers: 0"
And I should see "Your score is 0 (out of 1)."
And I should see "Your current grade is 0#0 out of 75"
Scenario: Current grade is displayed at end of lesson when grade type is set to scale
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the field "grade[modgrade_type]" to "Scale"
And I set the field "Scale" to "Test Scale"
And I press "Save and return to course"
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I press "Next page"
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 2 |
And I press "Submit"
And I press "Continue"
Then I should see "Congratulations - end of lesson reached"
And I should see "Your score is 1 (out of 1)."
And I should see "Your current grade is Excellent"
Scenario: Verify lesson summary with grade type set to none
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
# Since by default the grade type is point, change it to None.
And I set the field "grade[modgrade_type]" to "None"
And I press "Save and return to course"
# Answer the question incorrectly.
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I press "Next page"
And I set the following fields to these values:
| Your answer | 1 |
And I press "Submit"
And I press "Continue"
# Confirm the information displayed at the end of lesson when grade type is set to None.
Then I should see "Congratulations - end of lesson reached"
And I should see "Number of questions answered: 1"
And I should see "Number of correct answers: 0"
And I should see "Your score is 0 (out of 1)."
And I should not see "Your current grade is 0.0 out of 75"
@@ -0,0 +1,173 @@
@mod @mod_lesson
Feature: In a lesson activity, students can navigate through a series of pages in various ways depending upon their answers to questions
In order to create a lesson with conditional paths
As a teacher
I need to add pages and questions with links between them
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson name | C1 | lesson1 |
| page | <span class="multilang" lang="en">A page (EN)</span><span class="multilang" lang="eu">Orri bat (EU)</span> | C1 | PAGE1 |
Scenario: Student navigation with pages and questions
Given the "multilang" filter is "on"
And the "multilang" filter applies to "content and headings"
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | numeric | Hardest question ever | 1 + 1? |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Second page name | Previous page | | Previous page | 0 |
| Second page name | Next page | | Next page | 0 |
| Hardest question ever | 2 | Correct answer | End of lesson | 1 |
| Hardest question ever | 1 | Incorrect answer | Second page name | 0 |
When I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the field "Link to next activity" to "Page - A page (EN)"
And I press "Save and display"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
Then I should see "First page contents"
And I press "Next page"
And I should see "Second page contents"
And I should not see "First page contents"
And I press "Previous page"
And I should see "First page contents"
And I should not see "Second page contents"
And I press "Next page"
And I should see "Second page contents"
And I press "Next page"
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 1 |
And I press "Submit"
And I should see "Incorrect answer"
And I press "Continue"
And I should see "Second page name"
And I press "Next page"
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 2 |
And I press "Submit"
And I should see "Maximum number of attempts reached - Moving to next page"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 0 (out of 1)."
And I should see "Go to A page (EN)"
And I should not see "Orri bat (EU)"
Scenario: Student reattempts a question until out of attempts
Given the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | truefalse | Test question | Test content |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto | score |
| Test question | right | Next page | 1 |
| Test question | wrong | This page | 0 |
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| id_review | Yes |
| id_maxattempts | 3 |
And I press "Save and display"
When I am on the "Test lesson name" "lesson activity" page logged in as student1
Then I should see "Test content"
And I set the following fields to these values:
| wrong | 1 |
And I press "Submit"
And I should see "You have 2 attempt(s) remaining"
And I press "Yes, I'd like to try again"
And I should see "Test content"
And I set the following fields to these values:
| wrong | 1 |
And I press "Submit"
And I should see "You have 1 attempt(s) remaining"
And I press "Yes, I'd like to try again"
And I should see "Test content"
And I set the following fields to these values:
| wrong | 1 |
And I press "Submit"
And I should not see "Yes, I'd like to try again"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
Scenario: Student reattempts a question until out of attempts with specific jumps
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | truefalse | Test question | Test content 1 |
| Test lesson name | truefalse | Test question 2 | Test content 2 |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto | score |
| Test question | right | Next page | 1 |
| Test question | wrong | This page | 0 |
| Test question 2 | right | Test question | 1 |
| Test question 2 | wrong | Test question | 0 |
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| id_review | Yes |
| id_maxattempts | 3 |
And I press "Save and display"
When I am on the "Test lesson name" "lesson activity" page logged in as student1
Then I should see "Test content 1"
And I set the following fields to these values:
| right | 1 |
And I press "Submit"
And I should see "Test content 2"
And I set the following fields to these values:
| wrong | 1 |
And I press "Submit"
And I should see "You have 2 attempt(s) remaining"
And I press "Yes, I'd like to try again"
And I should see "Test content 2"
And I set the following fields to these values:
| wrong | 1 |
And I press "Submit"
And I should see "You have 1 attempt(s) remaining"
And I press "Yes, I'd like to try again"
And I should see "Test content 2"
And I set the following fields to these values:
| wrong | 1 |
And I press "Submit"
And I should not see "Yes, I'd like to try again"
And I press "Continue"
And I should see "Test content 1"
Scenario: Student should not see remaining attempts notification if maximum number of attempts is set to unlimited
Given the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | truefalse | Test question | Test content |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto | score |
| Test question | right | Next page | 1 |
| Test question | wrong | This page | 0 |
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| id_review | Yes |
| id_maxattempts | 0 |
And I press "Save and display"
When I am on the "Test lesson name" "lesson activity" page logged in as student1
Then I should see "Test content"
And I set the following fields to these values:
| wrong | 1 |
And I press "Submit"
And I should not see "attempt(s) remaining"
And I press "Yes, I'd like to try again"
And I should see "Test content"
And I set the following fields to these values:
| right | 1 |
And I press "Submit"
And I should not see "Yes, I'd like to try again"
And I should see "Congratulations - end of lesson reached"
@@ -0,0 +1,45 @@
@mod @mod_lesson
Feature: Lesson with no calendar capabilites
In order to allow work effectively
As a teacher
I need to be able to create lessons even when I cannot edit calendar events
Background:
Given the following "courses" exist:
| fullname | shortname | category | groupmode |
| Course 1 | C1 | 0 | 1 |
And the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
Given the following "activity" exists:
| activity | lesson |
| course | C1 |
| idnumber | 0001 |
| name | Test lesson name |
And I log in as "admin"
And I am on the "Course 1" "permissions" page
And I override the system permissions of "Teacher" role with:
| capability | permission |
| moodle/calendar:manageentries | Prohibit |
Scenario: Editing a lesson
Given I am on the "Test lesson name" "lesson activity editing" page logged in as admin
And I set the following fields to these values:
| id_available_enabled | 1 |
| id_available_day | 1 |
| id_available_month | 1 |
| id_available_year | 2017 |
| id_deadline_enabled | 1 |
| id_deadline_day | 1 |
| id_deadline_month | 2 |
| id_deadline_year | 2017 |
And I press "Save and return to course"
When I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| id_available_year | 2018 |
| id_deadline_year | 2018 |
And I press "Save and return to course"
Then I should see "Test lesson name"
@@ -0,0 +1,108 @@
@mod @mod_lesson
Feature: In Dashboard, teacher can see the number of student attempts to lessons
In order to know the number of student attempts to a lesson
As a teacher
I need to see it in Dashboard
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And I log in as "teacher1"
Scenario: number of student attempts
Given the following "activity" exists:
| activity | lesson |
| course | C1 |
| idnumber | 0001 |
| name | Test lesson name |
| retake | 1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | truefalse | True/false question 1 | Cat is an amphibian |
| Test lesson name | truefalse | True/false question 2 | Paper is made from trees. |
| Test lesson name | truefalse | True/false question 3 | 1+1=2 |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| True/false question 1 | False | Correct | Next page | 1 |
| True/false question 1 | True | Wrong | This page | 0 |
| True/false question 2 | True | Correct | Next page | 1 |
| True/false question 2 | False | Wrong | This page | 0 |
| True/false question 3 | True | Correct | Next page | 1 |
| True/false question 3 | False | Wrong | This page | 0 |
And I am on the "Test lesson name" "lesson activity editing" page
And I expand all fieldsets
And I set the following fields to these values:
| id_deadline_enabled | 1 |
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2030 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save and display"
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Paper is made from trees."
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "1+1=2"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 1 (out of 3)."
And I follow "Return to Course 1"
And I follow "Test lesson name"
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Paper is made from trees."
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "1+1=2"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 3 (out of 3)."
And I log out
And I am on the "Test lesson name" "lesson activity" page logged in as student2
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Paper is made from trees."
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "1+1=2"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 2 (out of 3)."
@@ -0,0 +1,131 @@
@mod @mod_lesson
Feature: In a lesson activity, I need to edit pages in the lesson taking into account locale settings
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "language customisations" exist:
| component | stringid | value |
| core_langconfig | decsep | # |
And the following "activities" exist:
| activity | name | course | idnumber | modattempts |
| lesson | Test lesson name | C1 | lesson1 | 1 |
And the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | numeric | Hardest question ever | 1 + 1? |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| Hardest question ever | 2.87 | Correct answer | End of lesson | 1 |
| Hardest question ever | 2.1:2.8 | Incorrect answer | This page | 0 |
Scenario: Create a numerical question with the locale specific variables
Given the following "activities" exist:
| activity | name | course | idnumber | modattempts |
| lesson | Empty lesson name | C1 | lesson2 | 1 |
And I am on the "Empty lesson name" "lesson activity" page logged in as teacher1
When I follow "Add a question page"
And I set the field "Select a question type" to "Numerical"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Second question |
| Page contents | 2 + 2? |
| id_answer_editor_0 | 4#87 |
| id_response_editor_0 | Correct answer |
| id_jumpto_0 | End of lesson |
| id_score_0 | 1 |
| id_answer_editor_1 | 4#1:4#8 |
| id_response_editor_1 | Incorrect answer |
| id_jumpto_1 | This page |
| id_score_1 | 0 |
And I press "Save page"
And I follow "Second question"
Then I should see "4#87"
And I should see "4#1:4#8"
Scenario: Edit a numerical question with the locale specific variables
Given I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I edit the lesson
And I follow "Hardest question ever"
Then I should see "2#87"
And I should see "2#1:2#8"
Scenario: View the detailed page of lesson
Given I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I edit the lesson
And I select edit type "Expanded"
Then I should see "2#87"
And I should see "2#1:2#8"
Scenario: Attempt the lesson successfully as a student
Given I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 2#87 |
And I press "Submit"
Then I should see "Correct answer"
And I should see "2#87"
And I should not see "Incorrect answer"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 1 (out of 1)."
Scenario: Attempt the lesson unsuccessfully as a student
Given I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 2#7 |
And I press "Submit"
Then I should not see "Correct answer"
And I should see "2#7"
And I should see "Incorrect answer"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 0 (out of 1)."
Scenario: Attempt the lesson successfully as a student and review
Given I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 2#87 |
And I press "Submit"
Then I should see "Correct answer"
And I should see "2#87"
And I should not see "Incorrect answer"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 1 (out of 1)."
And I follow "Review lesson"
Then I should see "1 + 1?"
And the following fields match these values:
| Your answer | 2#87 |
Scenario: Edit lesson question page with updated locale setting and wrong answer
Given the following "language customisations" exist:
| component | stringid | value |
| core_langconfig | decsep | , |
And I log in as "teacher1"
When I am on the "Test lesson name" "lesson activity" page
And I edit the lesson
And I follow "Hardest question ever"
Then I should see "2,87"
And I should see "2,1:2,8"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "1 + 1?"
And I set the following fields to these values:
| Your answer | 2,7 |
And I press "Submit"
And I should see "Incorrect answer"
And I should see "2,7"
And I should not see "Correct answer"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 0 (out of 1)."
@@ -0,0 +1,114 @@
@mod @mod_lesson
Feature: Teachers can review student progress on all lessons in a course by viewing the overview report
As a Teacher
I need to view the overview report for one of my students.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| activity | lesson |
| course | C1 |
| idnumber | 0001 |
| name | Test lesson name |
| retake | 1 |
And I am on the "Test lesson name" "lesson activity" page logged in as teacher1
Scenario: View student progress for lesson that was never attempted
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | truefalse | True/false question 1 | Paper is made from trees. |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| True/false question 1 | True | Correct | Next page | 1 |
| True/false question 1 | False | Wrong | This page | 0 |
When I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to course participants
And I follow "Student 1"
And I follow "Outline report"
Then I should see "No attempts have been made on this lesson"
Scenario: View student progress for an incomplete lesson containing both content and question pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | truefalse | True/false question 1 | Paper is made from trees. |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Second page name | Previous page | | Previous page | 0 |
| Second page name | Next page | | Next page | 0 |
| True/false question 1 | True | Correct | Next page | 1 |
| True/false question 1 | False | Wrong | This page | 0 |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
Then I am on the "Course 1" course page logged in as teacher1
And I navigate to course participants
And I follow "Student 1"
And I follow "Outline report"
And I should see "Lesson has been started, but not yet completed"
Scenario: View student progress for a lesson containing both content and question pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | truefalse | True/false question 1 | Paper is made from trees. |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Second page name | Previous page | | Previous page | 0 |
| Second page name | Next page | | Next page | 0 |
| True/false question 1 | True | Correct | Next page | 1 |
| True/false question 1 | False | Wrong | This page | 0 |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "Second page contents"
And I press "Next page"
And I should see "Paper is made from trees."
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
Then I am on the "Course 1" course page logged in as teacher1
And I navigate to course participants
And I follow "Student 1"
And I follow "Outline report"
And I should see "Grade: 100.00 / 100.00"
Scenario: View student attempts in a lesson containing only content pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto | score |
| First page name | Next page | Next page | 0 |
| Second page name | Previous page | Previous page | 0 |
| Second page name | End of lesson | End of lesson | 0 |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "Second page contents"
And I press "End of lesson"
Then I am on the "Course 1" course page logged in as teacher1
And I navigate to course participants
And I follow "Student 1"
And I follow "Outline report"
And I should see "Completed"
@@ -0,0 +1,77 @@
@mod @mod_lesson
Feature: Practice mode in a lesson activity
In order to improve my students understanding of a subject
As a teacher
I need to be able to set ungraded practice lesson activites
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| activity | lesson |
| course | C1 |
| idnumber | 0001 |
| name | Test lesson name |
And the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | truefalse | True or False | Paper is made from trees. |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto | score |
| True or False | True | Next page | 1 |
| True or False | False | This page | 0 |
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
Scenario: Non-practice lesson records grades in the gradebook
Given I set the following fields to these values:
| Name | Non-practice lesson |
| Description | This lesson will affect your course grade |
| Practice lesson | No |
And I press "Save and display"
When I am on the "Non-practice lesson" "lesson activity" page logged in as student1
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
Then I should see "View grades"
And I follow "Grades" in the user menu
And I am on "Course 1" course homepage
And I should see "Non-practice lesson"
Scenario: Practice lesson doesn't record grades in the gradebook
Given I set the following fields to these values:
| Name | Practice lesson |
| Description | This lesson will NOT affect your course grade |
| Practice lesson | Yes |
And I press "Save and display"
When I am on the "Practice lesson" "lesson activity" page logged in as student1
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
Then I should not see "View grades"
And I follow "Grades" in the user menu
And I click on "Course 1" "link" in the "Course 1" "table_row"
And I should not see "Practice lesson"
Scenario: Practice lesson with scale doesn't record grades in the gradebook
Given I set the following fields to these values:
| Name | Practice lesson with scale |
| Description | This lesson will NOT affect your course grade |
| Practice lesson | Yes |
| Type | Scale |
And I press "Save and display"
When I am on the "Practice lesson with scale" "lesson activity" page logged in as student1
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
Then I should not see "View grades"
And I follow "Grades" in the user menu
And I click on "Course 1" "link" in the "Course 1" "table_row"
And I should not see "Practice lesson with scale"
@@ -0,0 +1,59 @@
@mod @mod_lesson
Feature: In a lesson activity, students can see their progress viewing a progress bar.
In order to create a lesson with conditional paths
As a teacher
I need to add pages and questions with links between them
Scenario: Student navigation with progress bar
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson name | C1 | lesson1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | numeric | Hardest question ever | 1 + 1? |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Second page name | Previous page | | Previous page | 0 |
| Second page name | Next page | | Next page | 0 |
| Hardest question ever | 2 | Correct answer | End of lesson | 1 |
| Hardest question ever | 1 | Incorrect answer | First page name | 0 |
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Progress bar | Yes |
And I press "Save and display"
When I am on the "Test lesson name" "lesson activity" page logged in as student1
Then I should see "First page contents"
And I should see "You have completed 0% of the lesson"
And I press "Next page"
And I should see "Second page contents"
And I should see "You have completed 33% of the lesson"
And I press "Previous page"
And I should see "First page contents"
And I should see "You have completed 67% of the lesson"
And I press "Next page"
And I should see "Second page contents"
And I should see "You have completed 67% of the lesson"
And I press "Next page"
And I should see "1 + 1?"
And I should see "You have completed 67% of the lesson"
And I set the following fields to these values:
| Your answer | 2 |
And I press "Submit"
And I should see "Correct answer"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "You have completed 100% of the lesson"
@@ -0,0 +1,155 @@
@mod @mod_lesson
Feature: In a lesson activity, students can not re-attempt a question more than the allowed amount
In order to check a lesson question can not be attempted more than the allowed amount
As a student I need to check I cannot reattempt a question more than I should be allowed
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| activity | lesson |
| course | C1 |
| idnumber | 0001 |
| name | Test lesson name |
| retake | 1 |
| minquestions | 3 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | truefalse | True/false question 1 | The earth is round. |
| Test lesson name | truefalse | True/false question 2 | Kermit is a frog |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | truefalse | True/false question 3 | Paper is made from trees. |
| Test lesson name | content | Third page name | Third page contents |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Second page name | Previous page | | Previous page | 0 |
| Second page name | Next page | | Next page | 0 |
| True/false question 1 | True | Correct | Next page | 1 |
| True/false question 1 | False | Wrong | This page | 0 |
| True/false question 2 | True | Correct | Next page | 1 |
| True/false question 2 | False | Wrong | This page | 0 |
| True/false question 3 | True | Correct | Next page | 1 |
| True/false question 3 | False | Wrong | This page | 0 |
| Third page name | Previous page | | Previous page | 0 |
| Third page name | Next page | | Next page | 0 |
Scenario: Check that we can leave a quiz and when we re-enter we can not re-attempt the question again
Given I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "The earth is round"
And I set the following fields to these values:
| False| 1 |
And I press "Submit"
And I should see "Wrong"
And I am on the "Test lesson name" "lesson activity" page
And I should see "Do you want to start at the last page you saw?"
And I click on "No" "link" in the "#page-content" "css_element"
And I should see "First page contents"
And I press "Next page"
And I should see "The earth is round"
And I set the following fields to these values:
| False| 1 |
When I press "Submit"
Then I should see "Maximum number of attempts reached - Moving to next page"
@javascript @_bug_phantomjs
Scenario: Check that we can not click back on the browser at the last quiz result page and re-attempt the last question to get full marks
Given I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "The earth is round"
And I set the following fields to these values:
| True| 1 |
And I press "Submit"
And I should see "Correct"
And I press "Continue"
And I should see "Kermit is a frog"
And I set the following fields to these values:
| True| 1 |
And I press "Submit"
And I should see "Correct"
And I press "Continue"
And I should see "Second page contents"
And I press "Next page"
And I should see "Paper is made from trees"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I should see "Wrong"
And I press "Continue"
And I should see "Third page contents"
And I press "Next page"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 2 (out of 3)"
And I press the "back" button in the browser
And I press the "back" button in the browser
And I press the "back" button in the browser
And I reload the page
And I should see "Paper is made from trees"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I should see "Correct"
And I press "Continue"
And I should see "Third page contents"
When I press "Next page"
Then I should see "Number of questions answered: 1 (You should answer at least 3)"
@javascript
Scenario: Check that we can not click back on the browser and re-attempt a question
Given I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "The earth is round"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I should see "Wrong"
And I press the "back" button in the browser
And I reload the page
And I set the following fields to these values:
| True | 1 |
When I press "Submit"
Then I should see "Maximum number of attempts reached - Moving to next page"
And I press "Continue"
And I should see "Kermit is a frog"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I should see "Wrong"
And I press the "back" button in the browser
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I should see "Maximum number of attempts reached - Moving to next page"
And I press "Continue"
And I should see "Second page contents"
And I press "Next page"
And I should see "Paper is made from trees"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I should see "Correct"
And I press the "back" button in the browser
And I reload the page
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I should see "Maximum number of attempts reached - Moving to next page"
And I press "Continue"
And I should see "Third page contents"
And I press "Next page"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 1 (out of 3)"
@@ -0,0 +1,58 @@
@mod @mod_lesson
Feature: Set the maximum number of attempts for lesson activity question
In order to limit the number of attempts a student can take for lesson activity question
As a teacher
I should be able to set the maximum number of attempts for a lesson activity question
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
Scenario: Lesson activity question maximum number of attempts can be set
Given the following "activities" exist:
| activity | name | course | modattempts | review | maxattempts | feedback |
| lesson | Test lesson name | C1 | 0 | 1 | 2 | 1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | truefalse | Question 1 | Dolphins are mammals. |
| Test lesson name | truefalse | Question 2 | Trees are plants. |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| Question 1 | True | Right | Next page | 1 |
| Question 1 | False | Wrong | This page | 0 |
| Question 2 | True | Right | Next page | 1 |
| Question 2 | False | Wrong | This page | 0 |
And I am on the "Test lesson name" "lesson activity" page logged in as student1
# Answer lesson activity question 1 incorrectly.
When I set the following fields to these values:
| False | 1 |
And I press "Submit"
# Confirm you can still re-attempt the question 1.
Then I should see "You have 1 attempt(s) remaining"
And I press "Yes, I'd like to try again"
# Answer question 1 incorrectly again.
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
# Confirm you can't re-attempt the question 1 anymore.
And I should not see "Yes, I'd like to try again"
And I press "Continue"
# Answer question 2 correctly.
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I should not see "Yes, I'd like to try again"
# Complete attempt.
And I press "Continue"
And I am on the "Test lesson name" "lesson activity" page
# Confirm you can't see the question anymore.
And I should see "You are not allowed to retake this lesson."
@@ -0,0 +1,98 @@
@mod @mod_lesson
Feature: In a lesson activity, teachers can review student attempts
To review student attempts in a lesson
As a Teacher
I need to view the reports.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber | retake |
| lesson | Test lesson name | C1 | lesson1 | 1 |
Scenario: View student attempts in a lesson containing both content and question pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | content | Third page name | Third page contents |
| Test lesson name | truefalse | True/false question 1 | Paper is made from trees. |
| Test lesson name | truefalse | True/false question 2 | Kermit is a frog |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Second page name | Previous page | | Previous page | 0 |
| Second page name | Next page | | Next page | 0 |
| Third page name | Previous page | | Previous page | 0 |
| Third page name | Next page | | Next page | 0 |
| True/false question 1 | True | Correct | Next page | 1 |
| True/false question 1 | False | Wrong | This page | 0 |
| True/false question 2 | True | Correct | Next page | 1 |
| True/false question 2 | False | Wrong | This page | 0 |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "Second page contents"
And I press "Next page"
And I should see "Third page contents"
And I press "Next page"
And I should see "Paper is made from trees."
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Kermit is a frog"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
Then I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I navigate to "Reports" in current page administration
And I should see "Student 1"
And I should see "100%"
And I should see "High score"
And I should see "Average score"
And I should see "Low score"
Scenario: View student attempts in a lesson containing only content pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | content | Third page name | Third page contents |
| Test lesson name | content | Fourth page name | Fourth page contents |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto |
| First page name | Next page | Next page |
| Second page name | Previous page | Previous page |
| Second page name | Next page | Next page |
| Third page name | Previous page | Previous page |
| Third page name | Next page | Next page |
| Fourth page name | Previous page | Previous page |
| Fourth page name | End of lesson | End of lesson |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "Second page contents"
And I press "Next page"
And I should see "Third page contents"
And I press "Next page"
And I should see "Fourth page contents"
And I press "End of lesson"
Then I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I navigate to "Reports" in current page administration
And I should see "Student 1"
And I should not see "High score"
And I should not see "Average score"
And I should not see "Low score"
@@ -0,0 +1,42 @@
@mod @mod_lesson
Feature: In a lesson activity, teachers can view detailed statistics report
To review detailed statistics in a lesson
As a Teacher
I need to ve able to navigate to detailed statistics report page.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber | retake |
| lesson | Test lesson name | C1 | lesson1 | 1 |
And I am on the "Test lesson name" "lesson activity" page logged in as teacher1
Scenario: View detailed statistics in a lesson when empty string is given as answer
Given the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | numeric | Numerical question | What is 1 + 0.5? |
And the following "mod_lesson > answer" exist:
| page | answer | jumpto | score |
| Numerical question | 1.5 | End of lesson | 1 |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I press "Submit"
And I log out
And I am on the "Test lesson name" "lesson activity" page logged in as student2
And I set the field "Your answer" to "1.5"
And I press "Submit"
And I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I navigate to "Reports" in current page administration
And I select "Detailed statistics" from the "jump" singleselect
Then I should see "50% entered this."
@@ -0,0 +1,52 @@
@mod @mod_lesson
Feature: Lesson activity access can be restricted
In order to restrict access to lesson activity
As a teacher
I need to set the lesson activity restriction
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| Group 2 | C1 | G2 |
And the following "group members" exist:
| user | group |
| student1 | G1 |
| student2 | G2 |
And the following "activities" exist:
| activity | name | course | groupmode |
| lesson | Test lesson | C1 | 1 |
@javascript
Scenario Outline: Restrict lesson activity access by group
# Set lesson activity access by group
# Restrict lesson activity access by group as teacher
Given I am on the "Test lesson" "lesson activity editing" page logged in as teacher1
And I expand all fieldsets
And I click on "Add restriction..." "button"
And I click on "Group" "button" in the "Add restriction..." "dialogue"
# Set only Group 1 can access activity
And I set the field "Group" in the "Restrict access" "fieldset" to "Group 1"
And I click on "Displayed if student doesn't meet this condition" "button"
And I press "Save and return to course"
# Confirm that student1 can see lesson but student2 can't
When I am on the "Course 1" course page logged in as <user>
Then I <visibility> "Test lesson" in the "region-main" "region"
Examples:
| user | visibility |
| student1 | should see |
| student2 | should not see |
@@ -0,0 +1,82 @@
@mod @mod_lesson
Feature: Retake lesson activity
In order for student to retake a lesson activity
As a teacher
I should be able to allow retakes
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname |
| Course 1 | C1 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
# Generate lesson with retakes enabled, maxgrade = 100 and handling of re-takes = use mean
And the following "activities" exist:
| activity | name | course | retake | grade[modgrade_point] | usemaxgrade |
| lesson | Test lesson name | C1 | 1 | 100 | 0 |
# Generate question pages
And the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | multichoice | Question 1 | Which is not a plant? |
| Test lesson name | multichoice | Question 2 | Which is a plant? |
| Test lesson name | multichoice | Question 3 | Which is a plant and a colour? |
# Generate question answers
And the following "mod_lesson > answers" exist:
| page | answer | jumpto | score |
| Question 1 | Brown | Next page | 1 |
| Question 1 | Lavender | Next page | 0 |
| Question 2 | Brown | Next page | 0 |
| Question 2 | Lavender | Next page | 1 |
| Question 3 | Brown | Next page | 0 |
| Question 3 | Lavender | Next page | 1 |
Scenario: A student can retake a lesson
# First attempt - all correct
Given I am on the "Test lesson name" "lesson activity" page logged in as student1
And I set the following fields to these values:
| Brown | 1 |
And I press "Submit"
And I set the following fields to these values:
| Lavender | 1 |
And I press "Submit"
And I set the following fields to these values:
| Lavender | 1 |
And I press "Submit"
# Confirm that lesson can be retaken
When I am on the "Test lesson name" "lesson activity" page
Then I should see "Which is not a plant?"
# Second attempt - only 1 correct
And I set the following fields to these values:
| Lavender | 1 |
And I press "Submit"
And I set the following fields to these values:
| Brown | 1 |
And I press "Submit"
And I set the following fields to these values:
| Lavender | 1 |
And I press "Submit"
# Check that grade is 66.67 (mean of the 2 attempts)
And I click on "View grades" "link"
And "Test lesson name" row "Grade" column of "generaltable" table should contain "66.67"
# Change handling of re-takes = use maximum
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Handling of re-takes | Use maximum |
And I press "Save and display"
# Confirm that lesson grade is the maximum of the 2 attempts (100)
And I am on the "Course 1" "grades > user > View" page logged in as student1
And "Test lesson name" row "Grade" column of "generaltable" table should contain "100.00"
# Disable lesson retake
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Re-takes allowed | No |
And I press "Save and display"
# Confirm lesson cannot be retaken
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "You are not allowed to retake this lesson."
@@ -0,0 +1,91 @@
@mod @mod_lesson
Feature: In a lesson activity, students can review the answers they gave to questions
To review questions of a lesson
As a student
I need to complete a lesson answering all of the questions.
Scenario: Student answers questions and then reviews them.
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| activity | lesson |
| name | Test lesson name |
| course | C1 |
| idnumber | lesson1 |
# Display ongoing score = Yes
| ongoing | 1 |
# Slideshow = Yes
| slideshow | 1 |
# Maximum number of answers = 10
| maxanswers | 10 |
# Allow student review = Yes
| modattempts | 1 |
# Maximum number of attempts per question
| maxattempts | 3 |
# Custom scoring = No
| custom | 0 |
# Re-takes allowed = Yes
| retake | 1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | numeric | Hardest question ever | 1 + 1? |
| Test lesson name | truefalse | Next question | Paper is made from trees. |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| Hardest question ever | 2 | Correct answer 1 | Next page | 1 |
| Hardest question ever | 1 | Incorrect answer 1 | This page | 0 |
| Next question | True | Correct answer 2 | Next page | 1 |
| Next question | False | Incorrect answer 2 | This page | 0 |
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "You have answered 0 correctly out of 0 attempts."
And I should see "1 + 1?"
And I wait "1" seconds
And I set the following fields to these values:
| Your answer | 1 |
And I press "Submit"
And I should see "You have answered 0 correctly out of 1 attempts."
And I press "Continue"
And I should see "1 + 1?"
And I wait "1" seconds
And I set the following fields to these values:
| Your answer | 2 |
And I press "Submit"
And I should see "You have answered 1 correctly out of 2 attempts."
And I press "Continue"
And I should see "Paper is made from trees."
And I wait "1" seconds
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I should see "You have answered 2 correctly out of 3 attempts."
And I should see "Paper is made from trees."
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Number of questions answered: 2"
And I should see "Number of correct answers: 2"
And I should see "Your score is 2 (out of 3)."
When I follow "Review lesson"
Then I should see "You have answered 2 correctly out of 3 attempts."
And I should see "1 + 1?"
And I press "Next page"
And I should see "You have answered 2 correctly out of 3 attempts."
And I should see "1 + 1?"
And I should see "Correct answer 1"
And I press "Continue"
And I should see "You have answered 2 correctly out of 3 attempts."
And I should see "Paper is made from trees."
And I press "Next page"
And I should see "You have answered 2 correctly out of 3 attempts."
And I should see "Paper is made from trees."
And I should see "Correct answer 2"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
@@ -0,0 +1,250 @@
@mod @mod_lesson
Feature: In a lesson activity a student should
be able to close the lesson and then later resume.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| activity | lesson |
| name | Test lesson name |
| course | C1 |
| idnumber | 0001 |
| retake | 1 |
Scenario: resume a lesson with both content then question pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | content | Third page name | Third page contents |
| Test lesson name | truefalse | True/false question 1 | Paper is made from trees. |
| Test lesson name | truefalse | True/false question 2 | Kermit is a frog |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Second page name | Previous page | | Previous page | 0 |
| Second page name | Next page | | Next page | 0 |
| Third page name | Previous page | | Previous page | 0 |
| Third page name | Next page | | Next page | 0 |
| True/false question 1 | True | Correct | Next page | 1 |
| True/false question 1 | False | Wrong | This page | 0 |
| True/false question 2 | True | Correct | Next page | 1 |
| True/false question 2 | False | Wrong | This page | 0 |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
# Add 1 sec delay so lesson knows a valid attempt has been made in past.
And I wait "1" seconds
And I should see "Second page contents"
And I press "Next page"
And I should see "Third page contents"
And I am on "Course 1" course homepage
And I follow "Test lesson name"
And I should see "You have seen more than one page of this lesson already."
And I should see "Do you want to start at the last page you saw?"
And I follow "Yes"
Then I should see "Third page contents"
# Add 1 sec delay so lesson knows differentiate 3rd and paper attempts.
And I wait "1" seconds
And I press "Next page"
And I should see "Paper is made from trees."
And I am on "Course 1" course homepage
And I follow "Test lesson name"
And I should see "You have seen more than one page of this lesson already."
And I should see "Do you want to start at the last page you saw?"
And I follow "Yes"
And I should see "Paper is made from trees."
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Kermit is a frog"
And I am on "Course 1" course homepage
And I follow "Test lesson name"
And I should see "You have seen more than one page of this lesson already."
And I should see "Do you want to start at the last page you saw?"
And I follow "Yes"
And I should see "Kermit is a frog"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
Scenario: resume a lesson with only content pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | content | Third page name | Third page contents |
| Test lesson name | content | Fourth page name | Fourth page contents |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto |
| First page name | Next page | Next page |
| Second page name | Previous page | Previous page |
| Second page name | Next page | Next page |
| Third page name | Previous page | Previous page |
| Third page name | Next page | Next page |
| Fourth page name | Previous page | Previous page |
| Fourth page name | End of lesson | End of lesson |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
And I press "Next page"
And I should see "Second page contents"
# Add 1 sec delay so lesson knows a valid attempt has been made in past.
And I wait "1" seconds
And I press "Next page"
And I should see "Third page contents"
And I am on "Course 1" course homepage
And I follow "Test lesson name"
Then I should see "You have seen more than one page of this lesson already."
And I should see "Do you want to start at the last page you saw?"
And I follow "Yes"
And I should see "Third page contents"
And I press "Next page"
And I should see "Fourth page contents"
# Add 1 sec delay so lesson knows a valid attempt has been made in past.
And I wait "1" seconds
And I press "End of lesson"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "First page contents"
Scenario: resume a lesson with both question then content pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | truefalse | True/false question 1 | Cat is an amphibian |
| Test lesson name | content | First page name | First page contents |
| Test lesson name | truefalse | True/false question 2 | Paper is made from trees. |
| Test lesson name | truefalse | True/false question 3 | 1+1=2 |
| Test lesson name | truefalse | True/false question 4 | 2+2=4 |
| Test lesson name | content | Second page name | Second page contents |
| Test lesson name | truefalse | True/false question 5 | Kermit is a frog |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| True/false question 1 | False | Correct | Next page | 1 |
| True/false question 1 | True | Wrong | This page | 0 |
| First page name | Next page | | Next page | 0 |
| True/false question 2 | True | Correct | Next page | 1 |
| True/false question 2 | False | Wrong | This page | 0 |
| True/false question 3 | True | Correct | Next page | 1 |
| True/false question 3 | False | Wrong | This page | 0 |
| True/false question 4 | True | Correct | Next page | 1 |
| True/false question 4 | False | Wrong | This page | 0 |
| Second page name | Next page | | Next page | 0 |
| True/false question 5 | True | Correct | Next page | 1 |
| True/false question 5 | False | Wrong | This page | 0 |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "First page contents"
And I press "Next page"
And I should see "Paper is made from trees."
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "1+1=2"
And I set the following fields to these values:
| True | 1 |
# Add 1 sec delay so lesson knows a valid attempt has been made in past.
And I wait "1" seconds
And I press "Submit"
And I press "Continue"
And I should see "2+2=4"
And I am on "Course 1" course homepage
And I follow "Test lesson name"
And I should see "You have seen more than one page of this lesson already."
Then I should see "Do you want to start at the last page you saw?"
And I follow "Yes"
And I should see "2+2=4"
And I set the following fields to these values:
| True | 1 |
# Add 1 sec delay so lesson knows a valid attempt has been made in past.
And I wait "1" seconds
And I press "Submit"
And I press "Continue"
And I should see "Second page contents"
And I am on "Course 1" course homepage
And I follow "Test lesson name"
And I should see "You have seen more than one page of this lesson already."
And I should see "Do you want to start at the last page you saw?"
And I follow "Yes"
And I should see "Second page contents"
And I press "Next page"
And I should see "Kermit is a frog"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
Scenario: resume a lesson with only question pages
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | truefalse | True/false question 1 | Cat is an amphibian |
| Test lesson name | truefalse | True/false question 2 | Paper is made from trees. |
| Test lesson name | truefalse | True/false question 3 | 1+1=2 |
| Test lesson name | truefalse | True/false question 4 | 2+2=4 |
| Test lesson name | truefalse | True/false question 5 | Kermit is a frog |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| True/false question 1 | False | Correct | Next page | 1 |
| True/false question 1 | True | Wrong | This page | 0 |
| True/false question 2 | True | Correct | Next page | 1 |
| True/false question 2 | False | Wrong | This page | 0 |
| True/false question 3 | True | Correct | Next page | 1 |
| True/false question 3 | False | Wrong | This page | 0 |
| True/false question 4 | True | Correct | Next page | 1 |
| True/false question 4 | False | Wrong | This page | 0 |
| True/false question 5 | True | Correct | Next page | 1 |
| True/false question 5 | False | Wrong | This page | 0 |
When I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Paper is made from trees."
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "1+1=2"
And I set the following fields to these values:
| True | 1 |
# Add 1 sec delay so lesson knows a valid attempt has been made in past.
And I wait "1" seconds
And I press "Submit"
And I press "Continue"
And I should see "2+2=4"
And I am on "Course 1" course homepage
And I follow "Test lesson name"
Then I should see "You have seen more than one page of this lesson already."
And I should see "Do you want to start at the last page you saw?"
And I follow "Yes"
And I should see "2+2=4"
And I set the following fields to these values:
| True | 1 |
# Add 1 sec delay so lesson knows a valid attempt has been made in past.
And I wait "1" seconds
And I press "Submit"
And I press "Continue"
And I should see "Kermit is a frog"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
@@ -0,0 +1,336 @@
@mod @mod_lesson
Feature: Lesson user override
In order to grant a student special access to a lesson
As a teacher
I need to create an override for that user.
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Tina | Teacher1 | teacher1@example.com |
| student1 | Sam1 | Student1 | student1@example.com |
| student2 | Sam2 | Student2 | student2@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
| student2 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson name | C1 | lesson1 |
And the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | truefalse | True/false question 1 | Cat is an amphibian |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| True/false question 1 | False | Correct | Next page | 1 |
| True/false question 1 | True | Wrong | This page | 0 |
@javascript
Scenario: Add, modify then delete a user override
Given I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I navigate to "Overrides" in current page administration
And I follow "Add user override"
And I set the following fields to these values:
| Override user | Student1 |
| id_deadline_enabled | 1 |
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2020 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save"
And I should see "Wednesday, 1 January 2020, 8:00"
Then I click on "Edit" "link" in the "Sam1 Student1" "table_row"
And I set the following fields to these values:
| deadline[year] | 2030 |
And I press "Save"
And I should see "Tuesday, 1 January 2030, 8:00"
And I click on "Delete" "link"
And I press "Continue"
And I should not see "Sam1 Student1"
@javascript
Scenario: Duplicate a user override
Given I am on the "Test lesson name" "lesson activity" page logged in as teacher1
And I navigate to "Overrides" in current page administration
And I follow "Add user override"
And I set the following fields to these values:
| Override user | Student1 |
| id_deadline_enabled | 1 |
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2020 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save"
And I should see "Wednesday, 1 January 2020, 8:00"
Then I click on "copy" "link"
And I set the following fields to these values:
| Override user | Student2 |
| deadline[year] | 2030 |
And I press "Save"
And I should see "Tuesday, 1 January 2030, 8:00"
And I should see "Sam2 Student2"
@javascript
Scenario: Allow a single user to have re-take the lesson
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Re-takes allowed | 0 |
And I press "Save and display"
And I navigate to "Overrides" in current page administration
And I follow "Add user override"
And I set the following fields to these values:
| Override user | Student1 |
| Re-takes allowed | 1 |
And I press "Save"
And I should see "Re-takes allowed"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
When I am on the "Test lesson name" "lesson activity" page
Then I should not see "You are not allowed to retake this lesson."
And I should see "Cat is an amphibian"
And I am on the "Test lesson name" "lesson activity" page logged in as student2
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I am on the "Test lesson name" "lesson activity" page
And I should see "You are not allowed to retake this lesson."
@javascript
Scenario: Allow a single user to have a different password
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Password protected lesson | Yes |
| id_password | moodle_rules |
And I press "Save and display"
And I navigate to "Overrides" in current page administration
And I follow "Add user override"
And I set the following fields to these values:
| Override user | Student1 |
| Password protected lesson | 12345 |
And I press "Save"
And I should see "Password protected lesson"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
Then I should see "Test lesson name is a password protected lesson"
And I should not see "Cat is an amphibian"
And I set the field "userpassword" to "moodle_rules"
And I press "Continue"
And I should see "Login failed, please try again..."
And I should see "Test lesson name is a password protected lesson"
And I set the field "userpassword" to "12345"
And I press "Continue"
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| False | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I am on the "Test lesson name" "lesson activity" page logged in as student2
And I should see "Test lesson name is a password protected lesson"
And I should not see "Cat is an amphibian"
And I set the field "userpassword" to "12345"
And I press "Continue"
And I should see "Login failed, please try again..."
And I should see "Test lesson name is a password protected lesson"
And I set the field "userpassword" to "moodle_rules"
And I press "Continue"
@javascript
Scenario: Allow a user to have a different due date
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| id_deadline_enabled | 1 |
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2000 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save and display"
And I navigate to "Overrides" in current page administration
And I follow "Add user override"
And I set the following fields to these values:
| Override user | Student1 |
| id_deadline_enabled | 1 |
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2030 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save"
And I should see "Lesson closes"
And I am on the "Test lesson name" "lesson activity" page logged in as student2
And I wait until the page is ready
Then the activity date in "Test lesson name" should contain "Closed: Saturday, 1 January 2000, 8:00"
And I should not see "Cat is an amphibian"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
@javascript
Scenario: Allow a user to have a different start date
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| id_available_enabled | 1 |
| available[day] | 1 |
| available[month] | January |
| available[year] | 2030 |
| available[hour] | 08 |
| available[minute] | 00 |
And I press "Save and display"
And I navigate to "Overrides" in current page administration
And I follow "Add user override"
And I set the following fields to these values:
| Override user | Student1 |
| id_available_enabled | 1 |
| available[day] | 1 |
| available[month] | January |
| available[year] | 2015 |
| available[hour] | 08 |
| available[minute] | 00 |
And I press "Save"
And I should see "Lesson opens"
And I am on the "Test lesson name" "lesson activity" page logged in as student2
And I wait until the page is ready
Then the activity date in "Test lesson name" should contain "Opens: Tuesday, 1 January 2030, 8:00"
And I should not see "Cat is an amphibian"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
@javascript
Scenario: Allow a single user to have multiple attempts at each question
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Re-takes allowed | 1 |
And I press "Save and display"
And I navigate to "Overrides" in current page administration
And I follow "Add user override"
And I set the following fields to these values:
| Override user | Student1 |
| Maximum number of attempts per question | 2 |
And I press "Save"
And I should see "Maximum number of attempts per question"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I am on the "Test lesson name" "lesson activity" page logged in as student2
And I should see "Cat is an amphibian"
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
Then I press "Continue"
And I should see "Congratulations - end of lesson reached"
Scenario: Override a user when teacher is in no group, and does not have accessallgroups permission, and the activity's group mode is 'separate groups'
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/site:accessallgroups | Prevent | editingteacher | Course | C1 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | groupmode |
| lesson | Lesson 2 | Lesson 2 description | C1 | lesson2 | 1 |
When I am on the "Lesson 2" "lesson activity" page logged in as teacher1
And I navigate to "Overrides" in current page administration
Then I should see "No groups you can access."
And I should not see "Add user override"
Scenario: A teacher without accessallgroups permission should only be able to add user override for their group-mates, when the activity's group mode is 'separate groups'
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/site:accessallgroups | Prevent | editingteacher | Course | C1 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | groupmode |
| lesson | Lesson 2 | Lesson 2 description | C1 | lesson2 | 1 |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| Group 2 | C1 | G2 |
And the following "group members" exist:
| user | group |
| teacher1 | G1 |
| student1 | G1 |
| student2 | G2 |
When I am on the "Lesson 2" "lesson activity" page logged in as teacher1
And I navigate to "Overrides" in current page administration
And I follow "Add user override"
Then the "Override user" select box should contain "Sam1 Student1, student1@example.com"
And the "Override user" select box should not contain "Sam2 Student2, student2@example.com"
@javascript
Scenario: A teacher without accessallgroups permission should only be able to see the user override for their group-mates, when the activity's group mode is 'separate groups'
Given the following "permission overrides" exist:
| capability | permission | role | contextlevel | reference |
| moodle/site:accessallgroups | Prevent | editingteacher | Course | C1 |
And the following "activities" exist:
| activity | name | intro | course | idnumber | groupmode |
| lesson | Lesson 2 | Lesson 2 description | C1 | lesson2 | 1 |
And the following "groups" exist:
| name | course | idnumber |
| Group 1 | C1 | G1 |
| Group 2 | C1 | G2 |
And the following "group members" exist:
| user | group |
| teacher1 | G1 |
| student1 | G1 |
| student2 | G2 |
And I am on the "Lesson 2" "lesson activity" page logged in as admin
And I navigate to "Overrides" in current page administration
And I follow "Add user override"
And I set the following fields to these values:
| Override user | Student1 |
| id_deadline_enabled | 1 |
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2020 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save and enter another override"
And I set the following fields to these values:
| Override user | Student2 |
| id_deadline_enabled | 1 |
| deadline[day] | 1 |
| deadline[month] | January |
| deadline[year] | 2020 |
| deadline[hour] | 08 |
| deadline[minute] | 00 |
And I press "Save"
When I am on the "Lesson 2" "lesson activity" page logged in as teacher1
And I navigate to "Overrides" in current page administration
Then I should see "Student1" in the ".generaltable" "css_element"
And I should not see "Student2" in the ".generaltable" "css_element"
@javascript
Scenario: Create a user override when the lesson is not available to the student
Given I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the field "Availability" to "Hide on course page"
And I click on "Save and display" "button"
When I navigate to "Overrides" in current page administration
And I follow "Add user override"
And I set the following fields to these values:
| Override user | Student1 |
| Maximum number of attempts per question | 2 |
And I press "Save"
Then I should see "This override is inactive"
And "Edit" "icon" should exist in the "Sam1 Student1" "table_row"
And "copy" "icon" should exist in the "Sam1 Student1" "table_row"
And "Delete" "icon" should exist in the "Sam1 Student1" "table_row"
@@ -0,0 +1,92 @@
@mod @mod_lesson
Feature: In a lesson activity, students can see questions in random order
In order to create a lesson with clusters
As a teacher
I need to add content pages and questions with clusters and end of clusters pages
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Lesson with clusters | C1 | lesson1 |
And I log in as "teacher1"
Scenario: Lesson with two clusters
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Lesson with clusters | content | First page name | First page contents |
| Lesson with clusters | content | Second page name | Second page contents |
| Lesson with clusters | cluster | Cluster 1 | Cluster 1 |
| Lesson with clusters | multichoice | Question 1 | Question from cluster 1 |
| Lesson with clusters | multichoice | Question 2 | Question from cluster 1 |
| Lesson with clusters | endofcluster | End of cluster 1 | End of cluster 1 |
| Lesson with clusters | content | Third page name | Content page after cluster 1 |
| Lesson with clusters | cluster | Cluster 2 | Cluster 2 |
| Lesson with clusters | multichoice | Question 3 | Question from cluster 2 |
| Lesson with clusters | multichoice | Question 4 | Question from cluster 2 |
| Lesson with clusters | endofcluster | End of cluster 2 | End of cluster 2 |
| Lesson with clusters | content | Fourth page name | Content page after cluster 2 |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Second page name | Previous page | | Previous page | 0 |
| Second page name | Next page | | Next page | 0 |
| Cluster 1 | | | Unseen question within a cluster | 0 |
| Question 1 | Correct answer | Good | Cluster 1 | 1 |
| Question 1 | Incorrect answer | Bad | This page | 0 |
| Question 2 | Correct answer | Good | Cluster 1 | 1 |
| Question 2 | Incorrect answer | Bad | This page | 0 |
| End of cluster 1 | | | Next page | 0 |
| Third page name | Next page | | Next page | 0 |
| Cluster 2 | | | Unseen question within a cluster | 0 |
| Question 3 | Correct answer | Good | Unseen question within a cluster | 1 |
| Question 3 | Incorrect answer | Bad | This page | 0 |
| Question 4 | Correct answer | Good | Unseen question within a cluster | 1 |
| Question 4 | Incorrect answer | Bad | This page | 0 |
| End of cluster 2 | | | Next page | 0 |
| Fourth page name | Next page | | Next page | 0 |
When I am on the "Lesson with clusters" "lesson activity" page logged in as student1
Then I should see "First page contents"
And I press "Next page"
And I should see "Second page contents"
And I press "Next page"
And I should see "Question from cluster 1"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
And I should see "Question from cluster 1"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
And I should see "Content page after cluster 1"
And I press "Next page"
And I should see "Question from cluster 2"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
And I should see "Question from cluster 2"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
And I should see "Content page after cluster 2"
And I press "Next page"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 4 (out of 4)."
@@ -0,0 +1,89 @@
@mod @mod_lesson
Feature: In a lesson activity, students can see questions in random order and a single question drawn from a branch
In order to create a lesson with a cluster and a subcluster
As a teacher
I need to add content pages and questions with cluster, branchtable and end of branchtable and end of cluster pages
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Lesson with subcluster | C1 | lesson1 |
And I log in as "teacher1"
Scenario: Lesson with subcluster
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Lesson with subcluster | content | First page name | First page contents |
| Lesson with subcluster | cluster | Cluster | Cluster |
| Lesson with subcluster | multichoice | Question 1 | Question from cluster |
| Lesson with subcluster | multichoice | Question 2 | Question from cluster |
| Lesson with subcluster | content | Second page name | Beginning of the subcluster, should not be seen by students |
| Lesson with subcluster | multichoice | Question 3 | Question from cluster |
| Lesson with subcluster | multichoice | Question 4 | Question from cluster |
| Lesson with subcluster | multichoice | Question 5 | Question from cluster |
| Lesson with subcluster | endofbranch | End of branch | End of branch |
| Lesson with subcluster | multichoice | Question 6 | Question from cluster |
| Lesson with subcluster | endofcluster | End of cluster | End of cluster |
| Lesson with subcluster | content | Third page name | Content page after cluster |
And the following "mod_lesson > answers" exist:
| page | answer | response | jumpto | score |
| First page name | Next page | | Next page | 0 |
| Cluster | | | Unseen question within a cluster | 0 |
| Question 1 | Correct answer | Good | Cluster | 1 |
| Question 1 | Incorrect answer | Bad | This page | 0 |
| Question 2 | Correct answer | Good | Cluster | 1 |
| Question 2 | Incorrect answer | Bad | This page | 0 |
| Second page name | Next page | | Next page | 0 |
| Question 3 | Correct answer | Good | Cluster | 1 |
| Question 3 | Incorrect answer | Bad | This page | 0 |
| Question 4 | Correct answer | Good | Cluster | 1 |
| Question 4 | Incorrect answer | Bad | This page | 0 |
| Question 5 | Correct answer | Good | Cluster | 1 |
| Question 5 | Incorrect answer | Bad | This page | 0 |
| End of branch | | | Second page name | 0 |
| Question 6 | Correct answer | Good | Cluster | 1 |
| Question 6 | Incorrect answer | Bad | This page | 0 |
| End of cluster | | | Next page | 0 |
| Third page name | Next page | | Next page | 0 |
When I am on the "Lesson with subcluster" "lesson activity" page logged in as student1
Then I should see "First page contents"
And I press "Next page"
And I should see "Question from cluster"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
And I should see "Question from cluster"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
And I should see "Question from cluster"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
And I should see "Question from cluster"
And I set the following fields to these values:
| Correct answer | 1 |
And I press "Submit"
And I should see "Good"
And I press "Continue"
And I should see "Content page after cluster"
And I press "Next page"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 4 (out of 4)."
@@ -0,0 +1,85 @@
@mod @mod_lesson
Feature: link to gradebook on the end of lesson page
In order to allow students to see their lesson grades
As a teacher
I need to provide a link to gradebook on the end of lesson page
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson | C1 | lesson1 |
And the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson | content | First page name | First page contents |
| Test lesson | content | Second page name | Second page contents |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto |
| First page name | Next page | Next page |
| Second page name | Previous page | Previous page |
| Second page name | Next page | Next page |
Scenario: Link to gradebook for non practice lesson
When I am on the "Test lesson" "lesson activity" page logged in as student1
And I press "Next page"
And I press "Next page"
Then I should see "Congratulations - end of lesson reached"
And I should see "View grades"
And I follow "View grades"
And I should see "Student 1" in the "region-main" "region"
And I should see "Test lesson"
Scenario: No link to gradebook for non graded lesson
Given I am on the "Test lesson" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Type | None |
And I press "Save and display"
When I am on the "Test lesson" "lesson activity" page logged in as student1
And I press "Next page"
And I press "Next page"
Then I should see "Congratulations - end of lesson reached"
And I should not see "View grades"
Scenario: No link to gradebook for practice lesson
Given I am on the "Test lesson" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Practice lesson | Yes |
And I press "Save and display"
When I am on the "Test lesson" "lesson activity" page logged in as student1
And I press "Next page"
And I press "Next page"
Then I should see "Congratulations - end of lesson reached"
And I should not see "View grades"
Scenario: No link if Show gradebook to student disabled
Given I log in as "teacher1"
And I am on "Course 1" course homepage
And I navigate to "Settings" in current page administration
And I set the following fields to these values:
| Show gradebook to students | No |
And I press "Save and display"
When I am on the "Test lesson" "lesson activity" page logged in as student1
And I press "Next page"
And I press "Next page"
Then I should see "Congratulations - end of lesson reached"
And I should not see "View grades"
Scenario: No link to gradebook if no gradereport/user:view capability
Given the following "role capability" exists:
| role | student |
| gradereport/user:view | prevent |
When I am on the "Test lesson" "lesson activity" page logged in as student1
And I press "Next page"
And I press "Next page"
Then I should see "Congratulations - end of lesson reached"
And I should not see "View grades"
@@ -0,0 +1,41 @@
@mod @mod_lesson
Feature: A teacher can password protect a lesson
In order to avoid undesired accesses to lesson activities
As a teacher
I need to set a password to access the lesson
Scenario: Accessing as student to a protected lesson
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| activity | lesson |
| course | C1 |
| idnumber | 0001 |
| name | Test lesson |
| usepassword | 1 |
| password | moodle_rules |
Given the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson | content | First page name | First page contents |
And the following "mod_lesson > answer" exist:
| page | answer | jumpto |
| First page name | The first one | Next page |
When I am on the "Test lesson" "lesson activity" page logged in as student1
Then I should see "Test lesson is a password protected lesson"
And I should not see "First page contents"
And I set the field "userpassword" to "moodle"
And I press "Continue"
And I should see "Login failed, please try again..."
And I should see "Test lesson is a password protected lesson"
And I set the field "userpassword" to "moodle_rules"
And I press "Continue"
And I should see "First page contents"
@@ -0,0 +1,93 @@
@mod @mod_lesson
Feature: In a lesson activity, teacher can add embedded images in questions answers and responses
As a teacher
I need to add questions with images in answers and responses
@javascript @editor_tiny
Scenario: Questions with images in answers and responses
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activity" exists:
| course | C1 |
| activity | lesson |
| name | Test lesson name |
And the following "user private files" exist:
| user | filepath |
| teacher1 | mod/lesson/tests/fixtures/moodle_logo.jpg |
And I log in as "teacher1"
When I am on the "Test lesson name" "lesson activity" page
And I follow "Add a question page"
And I set the field "Select a question type" to "Multichoice"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Multichoice question |
| Page contents | What animal is an amphibian? |
| id_answer_editor_0 | Frog |
| id_response_editor_0 | Correct answer |
| id_jumpto_0 | Next page |
| id_score_0 | 1 |
| id_answer_editor_1 | Cat |
| id_response_editor_1 | Incorrect answer |
| id_jumpto_1 | This page |
| id_score_1 | 0 |
| id_answer_editor_2 | <p></p><p>Dog</p> |
| id_response_editor_2 | Incorrect answer |
| id_jumpto_2 | This page |
| id_score_2 | 0 |
And I click on "Image" "button" in the "//*[@data-fieldtype='editor']/*[descendant::*[@id='id_answer_editor_2']]" "xpath_element"
And I click on "Browse repositories" "button"
And I click on "Private files" "link" in the ".fp-repo-area" "css_element"
And I click on "moodle_logo.jpg" "link"
And I click on "Select this file" "button"
And I set the field "How would you describe this image to someone who can't see it?" to "It's the logo"
And I click on "Save" "button" in the "Image details" "dialogue"
And I press "Save page"
And I set the field "qtype" to "Add a question page"
And I set the field "Select a question type" to "True/false"
And I press "Add a question page"
And I set the following fields to these values:
| Page title | Next question |
| Page contents | Paper is made from trees. |
| id_answer_editor_0 | True |
| id_response_editor_0 | <p></p><p>Correct</p> |
| id_jumpto_0 | Next page |
| id_answer_editor_1 | False |
| id_response_editor_1 | Wrong |
| id_jumpto_1 | This page |
And I click on "Image" "button" in the "//*[@data-fieldtype='editor']/*[descendant::*[@id='id_response_editor_0']]" "xpath_element"
And I click on "Browse repositories" "button"
And I click on "Private files" "link" in the ".fp-repo-area" "css_element"
And I click on "moodle_logo.jpg" "link"
And I click on "Select this file" "button"
And I set the field "How would you describe this image to someone who can't see it?" to "It's the logo"
And I click on "Save" "button" in the "Image details" "dialogue"
And I press "Save page"
When I am on the "Test lesson name" "lesson activity" page logged in as student1
Then I should see "What animal is an amphibian?"
And "//*[contains(@class, 'answeroption')]//img[contains(@src, 'pluginfile.php')]" "xpath_element" should exist
And "//*[contains(@class, 'answeroption')]//img[contains(@src, 'moodle_logo.jpg')]" "xpath_element" should exist
And I set the following fields to these values:
| Cat | 1 |
And I press "Submit"
And I should see "Incorrect answer"
And I press "Continue"
And I should see "Paper is made from trees."
And I set the following fields to these values:
| True | 1 |
And I press "Submit"
And I should see "Correct"
And I should not see "Wrong"
And "//img[contains(@src, 'pluginfile.php')]" "xpath_element" should exist in the ".correctanswer" "css_element"
And "//img[contains(@src, 'moodle_logo.jpg')]" "xpath_element" should exist in the ".correctanswer" "css_element"
And I press "Continue"
And I should see "Congratulations - end of lesson reached"
And I should see "Your score is 1 (out of 2)."
@@ -0,0 +1,102 @@
@mod @mod_lesson
Feature: In a lesson activity, a non editing teacher can grade essay questions
As a non editing teacher
I need to grade student answers to essay questions in lesson
Scenario: non editing teacher grade essay questions
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| teacher2 | Teacher | 2 | teacher2@example.com |
| student1 | Student | 1 | student1@example.com |
| student2 | Student | 2 | student2@example.com |
| student3 | Student | 3 | student3@example.com |
| student4 | Student | 4 | student4@example.com |
| student5 | Student | 5 | student5@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| teacher2 | C1 | teacher |
| student1 | C1 | student |
| student2 | C1 | student |
| student3 | C1 | student |
| student4 | C1 | student |
| student5 | C1 | student |
And the following "groups" exist:
| name | course | idnumber | participation |
| Group A | C1 | G1 | 1 |
| Group B | C1 | G2 | 1 |
| Group C | C1 | G3 | 1 |
| Group D | C1 | G4 | 0 |
And the following "group members" exist:
| user | group |
| teacher1 | G1 |
| teacher2 | G2 |
| student1 | G1 |
| student2 | G2 |
| student3 | G3 |
| student4 | G4 |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson name | C1 | lesson1 |
And the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson name | essay | Essay question | <p>Please write a story about a <b>frog</b>.</p> |
And the following "mod_lesson > answer" exist:
| page | jumpto | score |
| Essay question | Next page | 1 |
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Group mode | Separate groups |
And I press "Save and display"
And I am on the "Test lesson name" "lesson activity" page logged in as student1
And I set the field "Your answer" to "<p>Once upon a time there was a little green frog."
And I press "Submit"
And I am on the "Test lesson name" "lesson activity" page logged in as student2
And I set the field "Your answer" to "<p>Once upon a time there were two little green frogs."
And I press "Submit"
And I am on the "Test lesson name" "lesson activity" page logged in as student4
And I set the field "Your answer" to "<p>Once upon a time there were four little green frogs."
And I press "Submit"
And I am on the "Test lesson name" "lesson activity" page logged in as student5
And I set the field "Your answer" to "<p>Once upon a time there were five little green frogs."
And I press "Submit"
When I am on the "Test lesson name" "lesson activity" page logged in as teacher1
Then I should see "Grade essays"
And I grade lesson essays
And I should see "Student 1"
And I should see "Student 2"
And I should see "Student 4"
And I should see "Student 5"
And I should see "Essay question"
And I click on "Essay question" "link" in the "Student 1" "table_row"
And I should see "Student 1's response"
And I should see "Once upon a time there was a little green frog."
And I set the following fields to these values:
| Your comments | Well done. |
| Essay score | 1 |
And I press "Save changes"
And I should see "Changes saved"
And I select "Group A" from the "Separate groups" singleselect
And I should see "Student 1"
And I should not see "Student 2"
And I should not see "Student 4"
And I should not see "Student 5"
And I select "Group B" from the "Separate groups" singleselect
And I should see "Student 2"
And I should not see "Student 1"
And I should not see "Student 4"
And I should not see "Student 5"
And I select "Group C" from the "Separate groups" singleselect
And I should see "No one in Group C has answered an essay question yet."
And I should not see "Group D" in the "Separate groups" "select"
And I am on the "Test lesson name" "lesson activity" page logged in as teacher2
Then I should see "Grade essays"
And I grade lesson essays
And I should not see "Student 1"
And I should see "Student 2"
And I should not see "Student 4"
And I should not see "Student 5"
+48
View File
@@ -0,0 +1,48 @@
@mod @mod_lesson
Feature: A teacher can set a time limit for a lesson
In order to restrict the time students have to complete a lesson
As a teacher
I need to set a time limit
@javascript
Scenario: Accessing as student to a lesson with time limit
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | course | name |
| lesson | C1 | Test lesson |
And the following "mod_lesson > page" exist:
| lesson | qtype | title | content |
| Test lesson | content | Lesson page name | Single lesson page contents |
And the following "mod_lesson > answer" exist:
| page | answer | jumpto |
| Lesson page name | Single button | This page |
And I am on the "Test lesson" "lesson activity editing" page logged in as teacher1
And I expand all fieldsets
And I set the following fields to these values:
| timelimit[enabled] | 1 |
| timelimit[timeunit] | 1 |
| timelimit[number] | 10 |
And I press "Save and display"
When I am on the "Test lesson" "lesson activity" page logged in as student1
Then I should see "You have 10 secs to finish the lesson."
And I wait "3" seconds
And I should see "Time remaining"
And I press "Single button"
And I should see "0:00:"
And I should see "Warning: You have 1 minute or less to finish the lesson."
And I wait "10" seconds
And I press "Single button"
And I should see "You ran out of time for this lesson."
And I should see "Your last answer may not have counted if it was answered after the time was up."
And I should see "Congratulations - end of lesson reached"
And I should not see "Single lesson page contents"
@@ -0,0 +1,61 @@
@mod @mod_lesson
Feature: An incorrect response to an answer with multiple attempts show appropriate continue buttons
In order for lesson the appropriate continue button to be displayed
As a teacher
I need to create a lesson with multiple attempts for each question
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| student1 | Student | 1 | student1@example.com |
| teacher1 | Teacher | 1 | teacher1@example.com |
And the following "courses" exist:
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| student1 | C1 | student |
And the following "activities" exist:
| activity | name | course | idnumber |
| lesson | Test lesson name | C1 | lesson1 |
And I am on the "Test lesson name" "lesson activity editing" page logged in as teacher1
And I set the following fields to these values:
| Provide option to try a question again | Yes |
| Maximum number of attempts per question | 2 |
And I press "Save and display"
Scenario: A student answering incorrectly to a question will see an option to move to the next question if set up.
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | numeric | Numerical question | What is 1 + 2? |
| Test lesson name | content | Just move on page | You are here to move on |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto | score |
| Numerical question | 3 | Next page | 1 |
| Numerical question | 2 | Next page | 0 |
| Just move on page | End this lesson | End of lesson | 0 |
And I am on the "Test lesson name" "lesson activity" page logged in as student1
When I set the field "Your answer" to "2"
And I press "Submit"
And I should see "That's the wrong answer"
And I should see "No, I just want to go on to the next question"
And I press "No, I just want to go on to the next question"
Then I should see "You are here to move on"
Scenario: A student answering incorrectly to a question will only see an option to try again if there is no matching wrong response.
Given the following "mod_lesson > pages" exist:
| lesson | qtype | title | content |
| Test lesson name | numeric | Numerical question | What is 1 + 2? |
| Test lesson name | content | Just move on page | You are here to move on |
And the following "mod_lesson > answers" exist:
| page | answer | jumpto | score |
| Numerical question | 3 | Next page | 1 |
| Just move on page | End this lesson | End of lesson | 0 |
And I am on the "Test lesson name" "lesson activity" page logged in as student1
When I set the field "Your answer" to "2"
And I press "Submit"
And I should see "That's the wrong answer"
Then I should not see "No, I just want to go on to the next question"
And I press "Yes, I'd like to try again"
And I should see "What is 1 + 2?"
+260
View File
@@ -0,0 +1,260 @@
<?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/>.
declare(strict_types = 1);
namespace mod_lesson;
use advanced_testcase;
use cm_info;
use coding_exception;
use mod_lesson\completion\custom_completion;
use moodle_exception;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/completionlib.php');
/**
* Class for unit testing mod_lesson/custom_completion.
*
* @package mod_lesson
* @copyright 2021 Michael Hawkins <michaelh@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
final class custom_completion_test extends advanced_testcase {
/**
* Data provider for get_state().
*
* @return array[]
*/
public static function get_state_provider(): array {
return [
'Undefined completion requirement' => [
'somenonexistentrule', COMPLETION_ENABLED, 3, null, coding_exception::class
],
'Minimum time spent requirement not available' => [
'completionstatusrequired', COMPLETION_DISABLED, 3, null, moodle_exception::class
],
'Minimum time spent required, user has not spent time in the lesson' => [
'completiontimespent', 30, false, COMPLETION_INCOMPLETE, null
],
'Minimum time spent required, user has not met completion requirement' => [
'completiontimespent', 30, 10, COMPLETION_INCOMPLETE, null
],
'Minimum time spent required, user has met completion requirement' => [
'completiontimespent', 30, 30, COMPLETION_COMPLETE, null
],
'Minimum time spent required, user has exceeded completion requirement' => [
'completiontimespent', 30, 40, COMPLETION_COMPLETE, null
],
'User must reach end of lesson, has not met completion requirement' => [
'completionendreached', 1, false, COMPLETION_INCOMPLETE, null
],
'User must reach end of lesson, has met completion requirement' => [
'completionendreached', 1, true, COMPLETION_COMPLETE, null
],
];
}
/**
* Test for get_state().
*
* @dataProvider get_state_provider
* @param string $rule The custom completion condition.
* @param int $rulevalue The custom completion rule value.
* @param mixed $uservalue The database value returned when checking the rule for the user.
* @param int|null $status Expected completion status for the rule.
* @param string|null $exception Expected exception.
*/
public function test_get_state(string $rule, int $rulevalue, $uservalue, ?int $status, ?string $exception): void {
global $DB;
if (!is_null($exception)) {
$this->expectException($exception);
}
// Custom completion rule data for cm_info::customdata.
$customdataval = [
'customcompletionrules' => [
$rule => $rulevalue
]
];
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->onlyMethods(['__get'])
->getMock();
// Mock the return of the magic getter method when fetching the cm_info object's
// customdata and instance values.
$mockcminfo->expects($this->any())
->method('__get')
->will($this->returnValueMap([
['customdata', $customdataval],
['instance', 1],
]));
if ($rule === 'completiontimespent') {
// Mock the DB call fetching user's lesson time spent.
$DB = $this->createMock(get_class($DB));
$DB->expects($this->atMost(1))
->method('get_field_sql')
->willReturn($uservalue);
} else if ($rule === 'completionendreached') {
// Mock the DB call fetching user's end reached state.
$DB = $this->createMock(get_class($DB));
$DB->expects($this->atMost(1))
->method('record_exists')
->willReturn($uservalue);
}
$customcompletion = new custom_completion($mockcminfo, 2);
$this->assertEquals($status, $customcompletion->get_state($rule));
}
/**
* Test for get_defined_custom_rules().
*/
public function test_get_defined_custom_rules(): void {
$expectedrules = [
'completiontimespent',
'completionendreached',
];
$definedrules = custom_completion::get_defined_custom_rules();
$this->assertCount(2, $definedrules);
foreach ($definedrules as $definedrule) {
$this->assertContains($definedrule, $expectedrules);
}
}
/**
* Test for get_defined_custom_rule_descriptions().
*/
public function test_get_custom_rule_descriptions(): void {
// Get defined custom rules.
$rules = custom_completion::get_defined_custom_rules();
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->onlyMethods(['__get'])
->getMock();
// Instantiate a custom_completion object using the mocked cm_info.
$customcompletion = new custom_completion($mockcminfo, 1);
// Get custom rule descriptions.
$ruledescriptions = $customcompletion->get_custom_rule_descriptions();
// Confirm that defined rules and rule descriptions are consistent with each other.
$this->assertEquals(count($rules), count($ruledescriptions));
foreach ($rules as $rule) {
$this->assertArrayHasKey($rule, $ruledescriptions);
}
}
/**
* Test for is_defined().
*/
public function test_is_defined(): void {
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->getMock();
$customcompletion = new custom_completion($mockcminfo, 1);
// All rules are defined.
$this->assertTrue($customcompletion->is_defined('completiontimespent'));
$this->assertTrue($customcompletion->is_defined('completionendreached'));
// Undefined rule is not found.
$this->assertFalse($customcompletion->is_defined('somerandomrule'));
}
/**
* Data provider for test_get_available_custom_rules().
*
* @return array[]
*/
public static function get_available_custom_rules_provider(): array {
return [
'No completion conditions enabled' => [
[
'completiontimespent' => COMPLETION_DISABLED,
'completionendreached' => COMPLETION_DISABLED,
],
[],
],
'Completion end reached enabled only' => [
[
'completiontimespent' => COMPLETION_DISABLED,
'completionendreached' => COMPLETION_ENABLED,
],
['completionendreached'],
],
'Completion time spent enabled only' => [
[
'completiontimespent' => 60,
'completionendreached' => COMPLETION_DISABLED,
],
['completiontimespent'],
],
'Completion end reached and time spent both enabled' => [
[
'completiontimespent' => 90,
'completionendreached' => COMPLETION_ENABLED,
],
['completiontimespent', 'completionendreached'],
],
];
}
/**
* Test for get_available_custom_rules().
*
* @dataProvider get_available_custom_rules_provider
* @param array $completionrulesvalues
* @param array $expected
*/
public function test_get_available_custom_rules(array $completionrulesvalues, array $expected): void {
$customcompletionrules = [
'customcompletionrules' => $completionrulesvalues,
];
// Build a mock cm_info instance.
$mockcminfo = $this->getMockBuilder(cm_info::class)
->disableOriginalConstructor()
->onlyMethods(['__get'])
->getMock();
// Mock the return of magic getter for the customdata attribute.
$mockcminfo->expects($this->any())
->method('__get')
->with('customdata')
->willReturn($customcompletionrules);
$customcompletion = new custom_completion($mockcminfo, 1);
$this->assertEquals($expected, $customcompletion->get_available_custom_rules());
}
}
+175
View File
@@ -0,0 +1,175 @@
<?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/>.
/**
* Contains unit tests for mod_lesson\dates.
*
* @package mod_lesson
* @category test
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
declare(strict_types=1);
namespace mod_lesson;
use advanced_testcase;
use cm_info;
use core\activity_dates;
/**
* Class for unit testing mod_lesson\dates.
*
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class dates_test extends advanced_testcase {
/**
* Data provider for get_dates_for_module().
* @return array[]
*/
public function get_dates_for_module_provider(): array {
$now = time();
$before = $now - DAYSECS;
$earlier = $before - DAYSECS;
$after = $now + DAYSECS;
$later = $after + DAYSECS;
return [
'without any dates' => [
null, null, null, null, null, null, []
],
'only with opening time' => [
$after, null, null, null, null, null, [
['label' => get_string('activitydate:opens', 'course'), 'timestamp' => $after, 'dataid' => 'available'],
]
],
'only with closing time' => [
null, $after, null, null, null, null, [
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $after, 'dataid' => 'deadline'],
]
],
'with both times' => [
$after, $later, null, null, null, null, [
['label' => get_string('activitydate:opens', 'course'), 'timestamp' => $after, 'dataid' => 'available'],
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later, 'dataid' => 'deadline'],
]
],
'between the dates' => [
$before, $after, null, null, null, null, [
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $before, 'dataid' => 'available'],
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $after, 'dataid' => 'deadline'],
]
],
'dates are past' => [
$earlier, $before, null, null, null, null, [
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier, 'dataid' => 'available'],
['label' => get_string('activitydate:closed', 'course'), 'timestamp' => $before, 'dataid' => 'deadline'],
]
],
'with user override' => [
$before, $after, $earlier, $later, null, null, [
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier, 'dataid' => 'available'],
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later, 'dataid' => 'deadline'],
]
],
'with group override' => [
$before, $after, null, null, $earlier, $later, [
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier, 'dataid' => 'available'],
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later, 'dataid' => 'deadline'],
]
],
'with both user and group overrides' => [
$before, $after, $earlier, $later, $earlier - DAYSECS, $later + DAYSECS, [
['label' => get_string('activitydate:opened', 'course'), 'timestamp' => $earlier, 'dataid' => 'available'],
['label' => get_string('activitydate:closes', 'course'), 'timestamp' => $later, 'dataid' => 'deadline'],
]
],
];
}
/**
* Test for get_dates_for_module().
*
* @dataProvider get_dates_for_module_provider
* @param int|null $available The 'available from' value of the lesson.
* @param int|null $deadline The lesson's deadline.
* @param int|null $useravailable The user override for opening the lesson.
* @param int|null $userdeadline The user override for deadline of the lesson.
* @param int|null $groupavailable The group override for opening the lesson.
* @param int|null $groupuserdeadline The group override for deadline of the lesson.
* @param array $expected The expected value of calling get_dates_for_module()
*/
public function test_get_dates_for_module(?int $available, ?int $deadline,
?int $useravailable, ?int $userdeadline,
?int $groupavailable, ?int $groupuserdeadline,
array $expected): void {
$this->resetAfterTest();
$generator = $this->getDataGenerator();
/** @var \mod_lesson_generator $lessongenerator */
$lessongenerator = $generator->get_plugin_generator('mod_lesson');
$course = $generator->create_course();
$user = $generator->create_user();
$generator->enrol_user($user->id, $course->id);
$data = ['course' => $course->id];
if ($available) {
$data['available'] = $available;
}
if ($deadline) {
$data['deadline'] = $deadline;
}
$this->setAdminUser();
$lesson = $lessongenerator->create_instance($data);
if ($useravailable || $userdeadline || $groupavailable || $groupuserdeadline) {
$generator->enrol_user($user->id, $course->id);
$group = $generator->create_group(['courseid' => $course->id]);
$generator->create_group_member(['groupid' => $group->id, 'userid' => $user->id]);
if ($useravailable || $userdeadline) {
$lessongenerator->create_override([
'lessonid' => $lesson->id,
'userid' => $user->id,
'available' => $useravailable,
'deadline' => $userdeadline,
]);
}
if ($groupavailable || $groupuserdeadline) {
$lessongenerator->create_override([
'lessonid' => $lesson->id,
'groupid' => $group->id,
'available' => $groupavailable,
'deadline' => $groupuserdeadline,
]);
}
}
$this->setUser($user);
$cm = get_coursemodule_from_instance('lesson', $lesson->id);
// Make sure we're using a cm_info object.
$cm = cm_info::create($cm);
$dates = activity_dates::get_dates_for_module($cm, (int) $user->id);
$this->assertEquals($expected, $dates);
}
}
+592
View File
@@ -0,0 +1,592 @@
<?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/>.
/**
* Events tests.
*
* @package mod_lesson
* @category test
* @copyright 2013 Mark Nelson <markn@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\event;
use lesson;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
class events_test extends \advanced_testcase {
/** @var stdClass the course used for testing */
private $course;
/** @var lesson the lesson used for testing */
private $lesson;
/**
* Test set up.
*
* This is executed before running any test in this file.
*/
public function setUp(): void {
$this->resetAfterTest();
$this->setAdminUser();
$this->course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $this->course->id));
// Convert to a lesson object.
$this->lesson = new lesson($lesson);
}
/**
* Test the page created event.
*
*/
public function test_page_created(): void {
// Set up a generator to create content.
$generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
// Trigger and capture the event.
$sink = $this->redirectEvents();
$pagerecord = $generator->create_content($this->lesson);
$page = $this->lesson->load_page($pagerecord->id);
// Get our event event.
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\page_created', $event);
$this->assertEquals($page->id, $event->objectid);
$this->assertEventContextNotUsed($event);
$this->assertDebuggingNotCalled();
}
/**
* Test the page created event.
*
*/
public function test_page_moved(): void {
// Set up a generator to create content.
// paga3 is the first one and page1 the last one.
$generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$pagerecord1 = $generator->create_content($this->lesson);
$page1 = $this->lesson->load_page($pagerecord1->id);
$pagerecord2 = $generator->create_content($this->lesson);
$page2 = $this->lesson->load_page($pagerecord2->id);
$pagerecord3 = $generator->create_content($this->lesson);
$page3 = $this->lesson->load_page($pagerecord3->id);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$this->lesson->resort_pages($page3->id, $pagerecord2->id);
// Get our event event.
$events = $sink->get_events();
$event = reset($events);
$this->assertCount(1, $events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\page_moved', $event);
$this->assertEquals($page3->id, $event->objectid);
$this->assertEquals($pagerecord1->id, $event->other['nextpageid']);
$this->assertEquals($pagerecord2->id, $event->other['prevpageid']);
$this->assertEventContextNotUsed($event);
$this->assertDebuggingNotCalled();
}
/**
* Test the page deleted event.
*
*/
public function test_page_deleted(): void {
// Set up a generator to create content.
$generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
// Create a content page.
$pagerecord = $generator->create_content($this->lesson);
// Get the lesson page information.
$page = $this->lesson->load_page($pagerecord->id);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$page->delete();
// Get our event event.
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\page_deleted', $event);
$this->assertEquals($page->id, $event->objectid);
$this->assertEventContextNotUsed($event);
$this->assertDebuggingNotCalled();
}
/**
* Test the page updated event.
*
* There is no external API for updateing a page, so the unit test will simply
* create and trigger the event and ensure data is returned as expected.
*/
public function test_page_updated(): void {
// Trigger an event: page updated.
$eventparams = array(
'context' => \context_module::instance($this->lesson->properties()->cmid),
'objectid' => 25,
'other' => array(
'pagetype' => 'True/false'
)
);
$event = \mod_lesson\event\page_updated::create($eventparams);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\page_updated', $event);
$this->assertEquals(25, $event->objectid);
$this->assertEquals('True/false', $event->other['pagetype']);
$this->assertEventContextNotUsed($event);
$this->assertDebuggingNotCalled();
}
/**
* Test the essay attempt viewed event.
*
* There is no external API for viewing an essay attempt, so the unit test will simply
* create and trigger the event and ensure the legacy log data is returned as expected.
*/
public function test_essay_attempt_viewed(): void {
// Create a essays list viewed event
$event = \mod_lesson\event\essay_attempt_viewed::create(array(
'objectid' => $this->lesson->id,
'relateduserid' => 3,
'context' => \context_module::instance($this->lesson->properties()->cmid),
'courseid' => $this->course->id
));
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\essay_attempt_viewed', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$this->assertEventContextNotUsed($event);
}
/**
* Test the lesson started event.
*/
public function test_lesson_started(): void {
// Trigger and capture the event.
$sink = $this->redirectEvents();
$this->lesson->start_timer();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\lesson_started', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$this->assertEventContextNotUsed($event);
}
/**
* Test the lesson restarted event.
*/
public function test_lesson_restarted(): void {
// Initialize timer.
$this->lesson->start_timer();
// Trigger and capture the event.
$sink = $this->redirectEvents();
$this->lesson->update_timer(true);
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\lesson_restarted', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$expected = array($this->course->id, 'lesson', 'start', 'view.php?id=' . $this->lesson->properties()->cmid,
$this->lesson->properties()->id, $this->lesson->properties()->cmid);
$this->assertEventContextNotUsed($event);
$this->assertDebuggingNotCalled();
}
/**
* Test the lesson restarted event.
*/
public function test_lesson_resumed(): void {
// Initialize timer.
$this->lesson->start_timer();
// Trigger and capture the event.
$sink = $this->redirectEvents();
$this->lesson->update_timer(true, true);
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\lesson_resumed', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$expected = array($this->course->id, 'lesson', 'start', 'view.php?id=' . $this->lesson->properties()->cmid,
$this->lesson->properties()->id, $this->lesson->properties()->cmid);
$this->assertEventContextNotUsed($event);
$this->assertDebuggingNotCalled();
}
/**
* Test the lesson ended event.
*/
public function test_lesson_ended(): void {
global $DB, $USER;
// Add a lesson timer so that stop_timer() does not complain.
$lessontimer = new \stdClass();
$lessontimer->lessonid = $this->lesson->properties()->id;
$lessontimer->userid = $USER->id;
$lessontimer->startime = time();
$lessontimer->lessontime = time();
$DB->insert_record('lesson_timer', $lessontimer);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$this->lesson->stop_timer();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\lesson_ended', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$this->assertEventContextNotUsed($event);
}
/**
* Test the essay assessed event.
*
* There is no external API for assessing an essay, so the unit test will simply
* create and trigger the event and ensure the legacy log data is returned as expected.
*/
public function test_essay_assessed(): void {
// Create an essay assessed event
$gradeid = 5;
$attemptid = 7;
$event = \mod_lesson\event\essay_assessed::create(array(
'objectid' => $gradeid,
'relateduserid' => 3,
'context' => \context_module::instance($this->lesson->properties()->cmid),
'courseid' => $this->course->id,
'other' => array(
'lessonid' => $this->lesson->id,
'attemptid' => $attemptid
)
));
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\essay_assessed', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$this->assertEventContextNotUsed($event);
}
/**
* Test the content page viewed event.
*
*/
public function test_content_page_viewed(): void {
global $DB, $PAGE;
// Set up a generator to create content.
$generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
// Create a content page.
$pagerecord = $generator->create_content($this->lesson);
// Get the lesson page information.
$page = $this->lesson->load_page($pagerecord->id);
// Get the coursemodule record to setup the $PAGE->cm.
$coursemodule = $DB->get_record('course_modules', array('id' => $this->lesson->properties()->cmid));
// Set the $PAGE->cm.
$PAGE->set_cm($coursemodule);
// Get the appropriate renderer.
$lessonoutput = $PAGE->get_renderer('mod_lesson');
// Trigger and capture the event.
$sink = $this->redirectEvents();
// Fire the function that leads to the triggering of our event.
$lessonoutput->display_page($this->lesson, $page, false);
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\content_page_viewed', $event);
$this->assertEquals($page->id, $event->objectid);
$this->assertEventContextNotUsed($event);
$this->assertDebuggingNotCalled();
}
/**
* Test the question viewed event.
*
*/
public function test_question_viewed(): void {
global $DB, $PAGE;
// Set up a generator to create content.
$generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
// Create a question page.
$pagerecord = $generator->create_question_truefalse($this->lesson);
// Get the lesson page information.
$page = $this->lesson->load_page($pagerecord->id);
// Get the coursemodule record to setup the $PAGE->cm.
$coursemodule = $DB->get_record('course_modules', array('id' => $this->lesson->properties()->cmid));
// Set the $PAGE->cm.
$PAGE->set_cm($coursemodule);
// Get the appropriate renderer.
$lessonoutput = $PAGE->get_renderer('mod_lesson');
// Trigger and capture the event.
$sink = $this->redirectEvents();
// Fire the function that leads to the triggering of our event.
$lessonoutput->display_page($this->lesson, $page, false);
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\question_viewed', $event);
$this->assertEquals($page->id, $event->objectid);
$this->assertEquals('True/false', $event->other['pagetype']);
$this->assertEventContextNotUsed($event);
$this->assertDebuggingNotCalled();
}
/**
* Test the question answered event.
*
* There is no external API for answering an truefalse question, so the unit test will simply
* create and trigger the event and ensure data is returned as expected.
*/
public function test_question_answered(): void {
// Trigger an event: truefalse question answered.
$eventparams = array(
'context' => \context_module::instance($this->lesson->properties()->cmid),
'objectid' => 25,
'other' => array(
'pagetype' => 'True/false'
)
);
$event = \mod_lesson\event\question_answered::create($eventparams);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\question_answered', $event);
$this->assertEquals(25, $event->objectid);
$this->assertEquals('True/false', $event->other['pagetype']);
$this->assertEventContextNotUsed($event);
$this->assertDebuggingNotCalled();
}
/**
* Test the user override created event.
*
* There is no external API for creating a user override, so the unit test will simply
* create and trigger the event and ensure the event data is returned as expected.
*/
public function test_user_override_created(): void {
$params = array(
'objectid' => 1,
'relateduserid' => 2,
'context' => \context_module::instance($this->lesson->properties()->cmid),
'other' => array(
'lessonid' => $this->lesson->id
)
);
$event = \mod_lesson\event\user_override_created::create($params);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\user_override_created', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$this->assertEventContextNotUsed($event);
}
/**
* Test the group override created event.
*
* There is no external API for creating a group override, so the unit test will simply
* create and trigger the event and ensure the event data is returned as expected.
*/
public function test_group_override_created(): void {
$params = array(
'objectid' => 1,
'context' => \context_module::instance($this->lesson->properties()->cmid),
'other' => array(
'lessonid' => $this->lesson->id,
'groupid' => 2
)
);
$event = \mod_lesson\event\group_override_created::create($params);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\group_override_created', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$this->assertEventContextNotUsed($event);
}
/**
* Test the user override updated event.
*
* There is no external API for updating a user override, so the unit test will simply
* create and trigger the event and ensure the event data is returned as expected.
*/
public function test_user_override_updated(): void {
$params = array(
'objectid' => 1,
'relateduserid' => 2,
'context' => \context_module::instance($this->lesson->properties()->cmid),
'other' => array(
'lessonid' => $this->lesson->id
)
);
$event = \mod_lesson\event\user_override_updated::create($params);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\user_override_updated', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$this->assertEventContextNotUsed($event);
}
/**
* Test the group override updated event.
*
* There is no external API for updating a group override, so the unit test will simply
* create and trigger the event and ensure the event data is returned as expected.
*/
public function test_group_override_updated(): void {
$params = array(
'objectid' => 1,
'context' => \context_module::instance($this->lesson->properties()->cmid),
'other' => array(
'lessonid' => $this->lesson->id,
'groupid' => 2
)
);
$event = \mod_lesson\event\group_override_updated::create($params);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$event->trigger();
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\group_override_updated', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$this->assertEventContextNotUsed($event);
}
/**
* Test the user override deleted event.
*/
public function test_user_override_deleted(): void {
global $DB;
// Create an override.
$override = new \stdClass();
$override->lesson = $this->lesson->id;
$override->userid = 2;
$override->id = $DB->insert_record('lesson_overrides', $override);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$this->lesson->delete_override($override->id);
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\user_override_deleted', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$this->assertEventContextNotUsed($event);
}
/**
* Test the group override deleted event.
*/
public function test_group_override_deleted(): void {
global $DB;
// Create an override.
$override = new \stdClass();
$override->lesson = $this->lesson->id;
$override->groupid = 2;
$override->id = $DB->insert_record('lesson_overrides', $override);
// Trigger and capture the event.
$sink = $this->redirectEvents();
$this->lesson->delete_override($override->id);
$events = $sink->get_events();
$event = reset($events);
// Check that the event data is valid.
$this->assertInstanceOf('\mod_lesson\event\group_override_deleted', $event);
$this->assertEquals(\context_module::instance($this->lesson->properties()->cmid), $event->get_context());
$this->assertEventContextNotUsed($event);
}
}
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 898 B

File diff suppressed because one or more lines are too long
+138
View File
@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<questestinterop>
<assessment title="sample_blackboard_six">
<section>
<item maxattempts="0">
<itemmetadata>
<bbmd_asi_object_id>DD76E663D4244C598FC91CFC433F6D5B</bbmd_asi_object_id>
<bbmd_asitype>Item</bbmd_asitype>
<bbmd_assessmenttype>Pool</bbmd_assessmenttype>
<bbmd_sectiontype>Subsection</bbmd_sectiontype>
<bbmd_questiontype>Fill in the Blank</bbmd_questiontype>
<bbmd_is_from_cartridge>false</bbmd_is_from_cartridge>
<qmd_absolutescore>0.0,1.0</qmd_absolutescore>
<qmd_absolutescore_min>0.0</qmd_absolutescore_min>
<qmd_absolutescore_max>1.0</qmd_absolutescore_max>
<qmd_assessmenttype>Proprietary</qmd_assessmenttype>
<qmd_itemtype>Logical Identifier</qmd_itemtype>
<qmd_levelofdifficulty>School</qmd_levelofdifficulty>
<qmd_maximumscore>0.0</qmd_maximumscore>
<qmd_numberofitems>0</qmd_numberofitems>
<qmd_renderingtype>Proprietary</qmd_renderingtype>
<qmd_responsetype>Single</qmd_responsetype>
<qmd_scoretype>Absolute</qmd_scoretype>
<qmd_status>Normal</qmd_status>
<qmd_timelimit>0</qmd_timelimit>
<qmd_weighting>0.0</qmd_weighting>
<qmd_typeofsolution>Complete</qmd_typeofsolution>
</itemmetadata>
<presentation>
<flow class="Block">
<flow class="QUESTION_BLOCK">
<flow class="FORMATTED_TEXT_BLOCK">
<material>
<mat_extension>
<mat_formattedtext type="HTML">&lt;span style="font-size:12pt"&gt;Name an amphibian&amp;#58; __________.&lt;/span&gt;</mat_formattedtext>
</mat_extension>
</material>
</flow>
<flow class="FILE_BLOCK">
<material/>
</flow>
<flow class="LINK_BLOCK">
<material>
<mattext charset="us-ascii" texttype="text/plain" uri="" xml:space="default"/>
</material>
</flow>
</flow>
<flow class="RESPONSE_BLOCK">
<response_str ident="response" rcardinality="Single" rtiming="No">
<render_fib charset="us-ascii" columns="127" encoding="UTF_8" fibtype="String" maxchars="0" maxnumber="0" minnumber="0" prompt="Box" rows="1"/>
</response_str>
</flow>
</flow>
</presentation>
<resprocessing scoremodel="SumOfScores">
<outcomes>
<decvar defaultval="0.0" maxvalue="1.0" minvalue="0.0" varname="SCORE" vartype="Decimal"/>
</outcomes>
<respcondition title="1CE934E53BDB437B8FD315E68063DA47">
<conditionvar>
<varequal case="No" respident="response">frog</varequal>
</conditionvar>
<displayfeedback feedbacktype="Response" linkrefid="correct"/>
<displayfeedback feedbacktype="Response" linkrefid="1CE934E53BDB437B8FD315E68063DA47"/>
</respcondition>
<respcondition title="incorrect">
<conditionvar>
<other/>
</conditionvar>
<setvar action="Set" variablename="SCORE">0.0</setvar>
<displayfeedback feedbacktype="Response" linkrefid="incorrect"/>
</respcondition>
</resprocessing>
<itemfeedback ident="correct" view="All">
<flow_mat class="Block">
<flow_mat class="FORMATTED_TEXT_BLOCK">
<material>
<mat_extension>
<mat_formattedtext type="HTML">A frog is an amphibian.</mat_formattedtext>
</mat_extension>
</material>
</flow_mat>
<flow_mat class="FILE_BLOCK">
<material/>
</flow_mat>
<flow_mat class="LINK_BLOCK">
<material>
<mattext charset="us-ascii" texttype="text/plain" uri="" xml:space="default"/>
</material>
</flow_mat>
</flow_mat>
</itemfeedback>
<itemfeedback ident="incorrect" view="All">
<flow_mat class="Block">
<flow_mat class="FORMATTED_TEXT_BLOCK">
<material>
<mat_extension>
<mat_formattedtext type="HTML">A frog is an amphibian.</mat_formattedtext>
</mat_extension>
</material>
</flow_mat>
<flow_mat class="FILE_BLOCK">
<material/>
</flow_mat>
<flow_mat class="LINK_BLOCK">
<material>
<mattext charset="us-ascii" texttype="text/plain" uri="" xml:space="default"/>
</material>
</flow_mat>
</flow_mat>
</itemfeedback>
<itemfeedback ident="1CE934E53BDB437B8FD315E68063DA47" view="All">
<solution feedbackstyle="Complete" view="All">
<solutionmaterial>
<flow_mat class="Block">
<flow_mat class="FORMATTED_TEXT_BLOCK">
<material>
<mat_extension>
<mat_formattedtext type="HTML">A frog is an amphibian.</mat_formattedtext>
</mat_extension>
</material>
</flow_mat>
<flow_mat class="FILE_BLOCK">
<material/>
</flow_mat>
<flow_mat class="LINK_BLOCK">
<material>
<mattext charset="us-ascii" texttype="text/plain" uri="" xml:space="default"/>
</material>
</flow_mat>
</flow_mat>
</solutionmaterial>
</solution>
</itemfeedback>
</item>
</section>
</assessment>
</questestinterop>
@@ -0,0 +1,65 @@
<?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/>.
/**
* Behat data generator for mod_lesson.
*
* @package mod_lesson
* @category test
* @copyright 2023 Dani Palou <dani@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Behat data generator for mod_lesson.
*
* @copyright 2023 Dani Palou <dani@nmoodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_mod_lesson_generator extends behat_generator_base {
/**
* Get a list of the entities that can be created.
*
* @return array entity name => information about how to generate.
*/
protected function get_creatable_entities(): array {
return [
'pages' => [
'singular' => 'page',
'datagenerator' => 'page',
'required' => ['lesson', 'qtype'],
'switchids' => ['lesson' => 'lessonid'],
],
'answers' => [
'singular' => 'answer',
'datagenerator' => 'answer',
'required' => ['page'],
],
];
}
/**
* Look up the id of a lesson from its name.
*
* @param string $idnumberorname the lesson idnumber or name, for example 'Test lesson'.
* @return int corresponding id.
*/
protected function get_lesson_id(string $idnumberorname): int {
return $this->get_cm_by_activity_name('lesson', $idnumberorname)->instance;
}
}
+805
View File
@@ -0,0 +1,805 @@
<?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/>.
/**
* mod_lesson data generator.
*
* @package mod_lesson
* @category test
* @copyright 2013 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
/**
* mod_lesson data generator class.
*
* @package mod_lesson
* @category test
* @copyright 2013 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_lesson_generator extends testing_module_generator {
/**
* @var int keep track of how many pages have been created.
*/
protected $pagecount = 0;
/**
* @var array list of candidate pages to be created when all answers have been added.
*/
protected $candidatepages = [];
/**
* @var array map of readable jumpto to integer value.
*/
protected $jumptomap = [
'This page' => LESSON_THISPAGE,
'Next page' => LESSON_NEXTPAGE,
'Previous page' => LESSON_PREVIOUSPAGE,
'End of lesson' => LESSON_EOL,
'Unseen question within a content page' => LESSON_UNSEENBRANCHPAGE,
'Random question within a content page' => LESSON_RANDOMPAGE,
'Random content page' => LESSON_RANDOMBRANCH,
'Unseen question within a cluster' => LESSON_CLUSTERJUMP,
];
/**
* To be called from data reset code only,
* do not use in tests.
* @return void
*/
public function reset() {
$this->pagecount = 0;
$this->candidatepages = [];
parent::reset();
}
/**
* Creates a lesson instance for testing purposes.
*
* @param null|array|stdClass $record data for module being generated.
* @param null|array $options general options for course module.
* @return stdClass record from module-defined table with additional field cmid (corresponding id in course_modules table)
*/
public function create_instance($record = null, array $options = null) {
global $CFG;
// Add default values for lesson.
$lessonconfig = get_config('mod_lesson');
$record = (array)$record + array(
'progressbar' => $lessonconfig->progressbar,
'ongoing' => $lessonconfig->ongoing,
'displayleft' => $lessonconfig->displayleftmenu,
'displayleftif' => $lessonconfig->displayleftif,
'slideshow' => $lessonconfig->slideshow,
'maxanswers' => $lessonconfig->maxanswers,
'feedback' => $lessonconfig->defaultfeedback,
'activitylink' => 0,
'available' => 0,
'deadline' => 0,
'usepassword' => 0,
'password' => '',
'dependency' => 0,
'timespent' => 0,
'completed' => 0,
'gradebetterthan' => 0,
'modattempts' => $lessonconfig->modattempts,
'review' => $lessonconfig->displayreview,
'maxattempts' => $lessonconfig->maximumnumberofattempts,
'nextpagedefault' => $lessonconfig->defaultnextpage,
'maxpages' => $lessonconfig->numberofpagestoshow,
'practice' => $lessonconfig->practice,
'custom' => $lessonconfig->customscoring,
'retake' => $lessonconfig->retakesallowed,
'usemaxgrade' => $lessonconfig->handlingofretakes,
'minquestions' => $lessonconfig->minimumnumberofquestions,
'grade' => 100,
);
if (!isset($record['mediafile'])) {
require_once($CFG->libdir.'/filelib.php');
$record['mediafile'] = file_get_unused_draft_itemid();
}
return parent::create_instance($record, (array)$options);
}
/**
* Creates a page for testing purposes. The page will be created when answers are added.
*
* @param null|array|stdClass $record data for page being generated.
* @param null|array $options general options.
*/
public function create_page($record = null, array $options = null) {
$record = (array) $record;
// Pages require answers to work. Add it as a candidate page to be created once answers have been added.
$record['answer_editor'] = [];
$record['response_editor'] = [];
$record['jumpto'] = [];
$record['score'] = [];
if (!isset($record['previouspage']) || $record['previouspage'] === '') {
// Previous page not set, set it to the last candidate page (if any).
$record['previouspage'] = empty($this->candidatepages) ? '0' : end($this->candidatepages)['title'];
}
$this->candidatepages[] = $record;
}
/**
* Creates a page and its answers for testing purposes.
*
* @param array $record data for page being generated.
* @return stdClass created page, null if couldn't be created because it has a jump to a page that doesn't exist.
* @throws coding_exception
*/
private function perform_create_page(array $record): ?stdClass {
global $DB;
$lesson = $DB->get_record('lesson', ['id' => $record['lessonid']], '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('lesson', $lesson->id);
$lesson->cmid = $cm->id;
$qtype = $record['qtype'];
unset($record['qtype']);
unset($record['lessonid']);
if (isset($record['content'])) {
$record['contents_editor'] = [
'text' => $record['content'],
'format' => FORMAT_MOODLE,
'itemid' => 0,
];
unset($record['content']);
}
$record['pageid'] = $this->get_previouspage_id($lesson->id, $record['previouspage']);
unset($record['previouspage']);
try {
$record['jumpto'] = $this->convert_page_jumpto($lesson->id, $record['jumpto']);
} catch (coding_exception $e) {
// This page has a jump to a page that hasn't been created yet.
return null;
}
switch ($qtype) {
case 'content':
case 'cluster':
case 'endofcluster':
case 'endofbranch':
$funcname = "create_{$qtype}";
break;
default:
$funcname = "create_question_{$qtype}";
}
if (!method_exists($this, $funcname)) {
throw new coding_exception('The page '.$record['title']." has an invalid qtype: $qtype");
}
return $this->{$funcname}($lesson, $record);
}
/**
* Creates a content page for testing purposes.
*
* @param stdClass $lesson instance where to create the page.
* @param array|stdClass $record data for page being generated.
* @return stdClass page record.
*/
public function create_content($lesson, $record = array()) {
global $DB, $CFG;
$now = time();
$this->pagecount++;
$record = (array)$record + array(
'lessonid' => $lesson->id,
'title' => 'Lesson page '.$this->pagecount,
'timecreated' => $now,
'qtype' => 20, // LESSON_PAGE_BRANCHTABLE
'pageid' => 0, // By default insert in the beginning.
);
if (!isset($record['contents_editor'])) {
$record['contents_editor'] = array(
'text' => 'Contents of lesson page '.$this->pagecount,
'format' => FORMAT_MOODLE,
'itemid' => 0,
);
}
$context = context_module::instance($lesson->cmid);
$page = lesson_page::create((object)$record, new lesson($lesson), $context, $CFG->maxbytes);
return $DB->get_record('lesson_pages', array('id' => $page->id), '*', MUST_EXIST);
}
/**
* Create True/false question pages.
* @param object $lesson
* @param array $record
* @return stdClass page record.
*/
public function create_question_truefalse($lesson, $record = array()) {
global $DB, $CFG;
$now = time();
$this->pagecount++;
$record = (array)$record + array(
'lessonid' => $lesson->id,
'title' => 'Lesson TF question '.$this->pagecount,
'timecreated' => $now,
'qtype' => 2, // LESSON_PAGE_TRUEFALSE.
'pageid' => 0, // By default insert in the beginning.
);
if (!isset($record['contents_editor'])) {
$record['contents_editor'] = array(
'text' => 'The answer is TRUE '.$this->pagecount,
'format' => FORMAT_HTML,
'itemid' => 0
);
}
// First Answer (TRUE).
if (!isset($record['answer_editor'][0])) {
$record['answer_editor'][0] = array(
'text' => 'TRUE answer for '.$this->pagecount,
'format' => FORMAT_HTML
);
}
if (!isset($record['jumpto'][0])) {
$record['jumpto'][0] = LESSON_NEXTPAGE;
}
// Second Answer (FALSE).
if (!isset($record['answer_editor'][1])) {
$record['answer_editor'][1] = array(
'text' => 'FALSE answer for '.$this->pagecount,
'format' => FORMAT_HTML
);
}
if (!isset($record['jumpto'][1])) {
$record['jumpto'][1] = LESSON_THISPAGE;
}
$context = context_module::instance($lesson->cmid);
$page = lesson_page::create((object)$record, new lesson($lesson), $context, $CFG->maxbytes);
return $DB->get_record('lesson_pages', array('id' => $page->id), '*', MUST_EXIST);
}
/**
* Create multichoice question pages.
* @param object $lesson
* @param array $record
* @return stdClass page record.
*/
public function create_question_multichoice($lesson, $record = array()) {
global $DB, $CFG;
$now = time();
$this->pagecount++;
$record = (array)$record + array(
'lessonid' => $lesson->id,
'title' => 'Lesson multichoice question '.$this->pagecount,
'timecreated' => $now,
'qtype' => 3, // LESSON_PAGE_MULTICHOICE.
'pageid' => 0, // By default insert in the beginning.
);
if (!isset($record['contents_editor'])) {
$record['contents_editor'] = array(
'text' => 'Pick the correct answer '.$this->pagecount,
'format' => FORMAT_HTML,
'itemid' => 0
);
}
// First Answer (correct).
if (!isset($record['answer_editor'][0])) {
$record['answer_editor'][0] = array(
'text' => 'correct answer for '.$this->pagecount,
'format' => FORMAT_HTML
);
}
if (!isset($record['jumpto'][0])) {
$record['jumpto'][0] = LESSON_NEXTPAGE;
}
// Second Answer (incorrect).
if (!isset($record['answer_editor'][1])) {
$record['answer_editor'][1] = array(
'text' => 'correct answer for '.$this->pagecount,
'format' => FORMAT_HTML
);
}
if (!isset($record['jumpto'][1])) {
$record['jumpto'][1] = LESSON_THISPAGE;
}
$context = context_module::instance($lesson->cmid);
$page = lesson_page::create((object)$record, new lesson($lesson), $context, $CFG->maxbytes);
return $DB->get_record('lesson_pages', array('id' => $page->id), '*', MUST_EXIST);
}
/**
* Create essay question pages.
* @param object $lesson
* @param array $record
* @return stdClass page record.
*/
public function create_question_essay($lesson, $record = array()) {
global $DB, $CFG;
$now = time();
$this->pagecount++;
$record = (array)$record + array(
'lessonid' => $lesson->id,
'title' => 'Lesson Essay question '.$this->pagecount,
'timecreated' => $now,
'qtype' => 10, // LESSON_PAGE_ESSAY.
'pageid' => 0, // By default insert in the beginning.
);
if (!isset($record['contents_editor'])) {
$record['contents_editor'] = array(
'text' => 'Write an Essay '.$this->pagecount,
'format' => FORMAT_HTML,
'itemid' => 0
);
}
// Essays have an answer of NULL.
if (!isset($record['answer_editor'][0])) {
$record['answer_editor'][0] = array(
'text' => null,
'format' => FORMAT_MOODLE
);
}
if (!isset($record['jumpto'][0])) {
$record['jumpto'][0] = LESSON_NEXTPAGE;
}
$context = context_module::instance($lesson->cmid);
$page = lesson_page::create((object)$record, new lesson($lesson), $context, $CFG->maxbytes);
return $DB->get_record('lesson_pages', array('id' => $page->id), '*', MUST_EXIST);
}
/**
* Create matching question pages.
* @param object $lesson
* @param array $record
* @return stdClass page record.
*/
public function create_question_matching($lesson, $record = array()) {
global $DB, $CFG;
$now = time();
$this->pagecount++;
$record = (array)$record + array(
'lessonid' => $lesson->id,
'title' => 'Lesson Matching question '.$this->pagecount,
'timecreated' => $now,
'qtype' => 5, // LESSON_PAGE_MATCHING.
'pageid' => 0, // By default insert in the beginning.
);
if (!isset($record['contents_editor'])) {
$record['contents_editor'] = array(
'text' => 'Match the values '.$this->pagecount,
'format' => FORMAT_HTML,
'itemid' => 0
);
}
// Feedback for correct result.
if (!isset($record['answer_editor'][0])) {
$record['answer_editor'][0] = array(
'text' => '',
'format' => FORMAT_HTML
);
}
// Feedback for wrong result.
if (!isset($record['answer_editor'][1])) {
$record['answer_editor'][1] = array(
'text' => '',
'format' => FORMAT_HTML
);
}
// First answer value.
if (!isset($record['answer_editor'][2])) {
$record['answer_editor'][2] = array(
'text' => 'Match value 1',
'format' => FORMAT_HTML
);
}
// First response value.
if (!isset($record['response_editor'][2])) {
$record['response_editor'][2] = 'Match answer 1';
}
// Second Matching value.
if (!isset($record['answer_editor'][3])) {
$record['answer_editor'][3] = array(
'text' => 'Match value 2',
'format' => FORMAT_HTML
);
}
// Second Matching answer.
if (!isset($record['response_editor'][3])) {
$record['response_editor'][3] = 'Match answer 2';
}
// Jump Values.
if (!isset($record['jumpto'][0])) {
$record['jumpto'][0] = LESSON_NEXTPAGE;
}
if (!isset($record['jumpto'][1])) {
$record['jumpto'][1] = LESSON_THISPAGE;
}
// Mark the correct values.
if (!isset($record['score'][0])) {
$record['score'][0] = 1;
}
$context = context_module::instance($lesson->cmid);
$page = lesson_page::create((object)$record, new lesson($lesson), $context, $CFG->maxbytes);
return $DB->get_record('lesson_pages', array('id' => $page->id), '*', MUST_EXIST);
}
/**
* Create shortanswer question pages.
* @param object $lesson
* @param array $record
* @return stdClass page record.
*/
public function create_question_shortanswer($lesson, $record = array()) {
global $DB, $CFG;
$now = time();
$this->pagecount++;
$record = (array)$record + array(
'lessonid' => $lesson->id,
'title' => 'Lesson Shortanswer question '.$this->pagecount,
'timecreated' => $now,
'qtype' => 1, // LESSON_PAGE_SHORTANSWER.
'pageid' => 0, // By default insert in the beginning.
);
if (!isset($record['contents_editor'])) {
$record['contents_editor'] = array(
'text' => 'Fill in the blank '.$this->pagecount,
'format' => FORMAT_HTML,
'itemid' => 0
);
}
// First Answer (correct).
if (!isset($record['answer_editor'][0])) {
$record['answer_editor'][0] = array(
'text' => 'answer'.$this->pagecount,
'format' => FORMAT_MOODLE
);
}
if (!isset($record['jumpto'][0])) {
$record['jumpto'][0] = LESSON_NEXTPAGE;
}
$context = context_module::instance($lesson->cmid);
$page = lesson_page::create((object)$record, new lesson($lesson), $context, $CFG->maxbytes);
return $DB->get_record('lesson_pages', array('id' => $page->id), '*', MUST_EXIST);
}
/**
* Create shortanswer question pages.
* @param object $lesson
* @param array $record
* @return stdClass page record.
*/
public function create_question_numeric($lesson, $record = array()) {
global $DB, $CFG;
$now = time();
$this->pagecount++;
$record = (array)$record + array(
'lessonid' => $lesson->id,
'title' => 'Lesson numerical question '.$this->pagecount,
'timecreated' => $now,
'qtype' => 8, // LESSON_PAGE_NUMERICAL.
'pageid' => 0, // By default insert in the beginning.
);
if (!isset($record['contents_editor'])) {
$record['contents_editor'] = array(
'text' => 'Numerical question '.$this->pagecount,
'format' => FORMAT_HTML,
'itemid' => 0
);
}
// First Answer (correct).
if (!isset($record['answer_editor'][0])) {
$record['answer_editor'][0] = array(
'text' => $this->pagecount,
'format' => FORMAT_MOODLE
);
}
if (!isset($record['jumpto'][0])) {
$record['jumpto'][0] = LESSON_NEXTPAGE;
}
$context = context_module::instance($lesson->cmid);
$page = lesson_page::create((object)$record, new lesson($lesson), $context, $CFG->maxbytes);
return $DB->get_record('lesson_pages', array('id' => $page->id), '*', MUST_EXIST);
}
/**
* Creates a cluster page for testing purposes.
*
* @param stdClass $lesson instance where to create the page.
* @param array $record data for page being generated.
* @return stdClass page record.
*/
public function create_cluster(stdClass $lesson, array $record = []): stdClass {
global $DB, $CFG;
$now = time();
$this->pagecount++;
$record = $record + [
'lessonid' => $lesson->id,
'title' => 'Cluster '.$this->pagecount,
'timecreated' => $now,
'qtype' => 30, // LESSON_PAGE_CLUSTER.
'pageid' => 0, // By default insert in the beginning.
];
if (!isset($record['contents_editor'])) {
$record['contents_editor'] = [
'text' => 'Cluster '.$this->pagecount,
'format' => FORMAT_MOODLE,
'itemid' => 0,
];
}
$context = context_module::instance($lesson->cmid);
$page = lesson_page::create((object)$record, new lesson($lesson), $context, $CFG->maxbytes);
return $DB->get_record('lesson_pages', ['id' => $page->id], '*', MUST_EXIST);
}
/**
* Creates a end of cluster page for testing purposes.
*
* @param stdClass $lesson instance where to create the page.
* @param array $record data for page being generated.
* @return stdClass page record.
*/
public function create_endofcluster(stdClass $lesson, array $record = []): stdClass {
global $DB, $CFG;
$now = time();
$this->pagecount++;
$record = $record + [
'lessonid' => $lesson->id,
'title' => 'End of cluster '.$this->pagecount,
'timecreated' => $now,
'qtype' => 31, // LESSON_PAGE_ENDOFCLUSTER.
'pageid' => 0, // By default insert in the beginning.
];
if (!isset($record['contents_editor'])) {
$record['contents_editor'] = [
'text' => 'End of cluster '.$this->pagecount,
'format' => FORMAT_MOODLE,
'itemid' => 0,
];
}
$context = context_module::instance($lesson->cmid);
$page = lesson_page::create((object)$record, new lesson($lesson), $context, $CFG->maxbytes);
return $DB->get_record('lesson_pages', ['id' => $page->id], '*', MUST_EXIST);
}
/**
* Creates a end of branch page for testing purposes.
*
* @param stdClass $lesson instance where to create the page.
* @param array $record data for page being generated.
* @return stdClass page record.
*/
public function create_endofbranch(stdClass $lesson, array $record = []): stdClass {
global $DB, $CFG;
$now = time();
$this->pagecount++;
$record = $record + [
'lessonid' => $lesson->id,
'title' => 'End of branch '.$this->pagecount,
'timecreated' => $now,
'qtype' => 21, // LESSON_PAGE_ENDOFBRANCH.
'pageid' => 0, // By default insert in the beginning.
];
if (!isset($record['contents_editor'])) {
$record['contents_editor'] = [
'text' => 'End of branch '.$this->pagecount,
'format' => FORMAT_MOODLE,
'itemid' => 0,
];
}
$context = context_module::instance($lesson->cmid);
$page = lesson_page::create((object)$record, new lesson($lesson), $context, $CFG->maxbytes);
return $DB->get_record('lesson_pages', ['id' => $page->id], '*', MUST_EXIST);
}
/**
* Create a lesson override (either user or group).
*
* @param array $data must specify lessonid, and one of userid or groupid.
* @throws coding_exception
*/
public function create_override(array $data): void {
global $DB;
if (!isset($data['lessonid'])) {
throw new coding_exception('Must specify lessonid when creating a lesson override.');
}
if (!isset($data['userid']) && !isset($data['groupid'])) {
throw new coding_exception('Must specify one of userid or groupid when creating a lesson override.');
}
if (isset($data['userid']) && isset($data['groupid'])) {
throw new coding_exception('Cannot specify both userid and groupid when creating a lesson override.');
}
$DB->insert_record('lesson_overrides', (object) $data);
}
/**
* Creates an answer in a page for testing purposes.
*
* @param null|array|stdClass $record data for module being generated.
* @param null|array $options general options.
* @throws coding_exception
*/
public function create_answer($record = null, array $options = null) {
$record = (array) $record;
$candidatepage = null;
$pagetitle = $record['page'];
$found = false;
foreach ($this->candidatepages as &$candidatepage) {
if ($candidatepage['title'] === $pagetitle) {
$found = true;
break;
}
}
if (!$found) {
throw new coding_exception("Page '$pagetitle' not found in candidate pages. Please make sure the page exists "
. 'and all answers are in the same table.');
}
if (isset($record['answer'])) {
$candidatepage['answer_editor'][] = [
'text' => $record['answer'],
'format' => FORMAT_HTML,
];
} else {
$candidatepage['answer_editor'][] = null;
}
if (isset($record['response'])) {
$candidatepage['response_editor'][] = [
'text' => $record['response'],
'format' => FORMAT_HTML,
];
} else {
$candidatepage['response_editor'][] = null;
}
$candidatepage['jumpto'][] = $record['jumpto'] ?? LESSON_THISPAGE;
$candidatepage['score'][] = $record['score'] ?? 0;
}
/**
* All answers in a table have been generated, create the pages.
*/
public function finish_generate_answer() {
$this->create_candidate_pages();
}
/**
* Create candidate pages.
*
* @throws coding_exception
*/
protected function create_candidate_pages(): void {
// For performance reasons it would be better to use a topological sort algorithm. But since test cases shouldn't have
// a lot of paged and complex jumps it was implemented using a simpler approach.
$consecutiveblocked = 0;
while (count($this->candidatepages) > 0) {
$page = array_shift($this->candidatepages);
$id = $this->perform_create_page($page);
if ($id === null) {
// Page cannot be created yet because of jumpto. Move it to the end of list.
$consecutiveblocked++;
$this->candidatepages[] = $page;
if ($consecutiveblocked === count($this->candidatepages)) {
throw new coding_exception('There is a circular dependency in pages jumps.');
}
} else {
$consecutiveblocked = 0;
}
}
}
/**
* Calculate the previous page id.
* If no page title is supplied, use the last page created in the lesson (0 if no pages).
* If page title is supplied, search it in DB and the list of candidate pages.
*
* @param int $lessonid the lesson id.
* @param string $pagetitle the page title, for example 'Test page'. '0' if no previous page.
* @return int corresponding id. 0 if no previous page.
* @throws coding_exception
*/
protected function get_previouspage_id(int $lessonid, string $pagetitle): int {
global $DB;
if (is_numeric($pagetitle) && intval($pagetitle) === 0) {
return 0;
}
$pages = $DB->get_records('lesson_pages', ['lessonid' => $lessonid, 'title' => $pagetitle], 'id ASC', 'id, title');
if (count($pages) > 1) {
throw new coding_exception("More than one page with '$pagetitle' found");
} else if (!empty($pages)) {
return current($pages)->id;
}
// Page doesn't exist, search if it's a candidate page. If it is, use its previous page instead.
foreach ($this->candidatepages as $candidatepage) {
if ($candidatepage['title'] === $pagetitle) {
return $this->get_previouspage_id($lessonid, $candidatepage['previouspage']);
}
}
throw new coding_exception("Page '$pagetitle' not found");
}
/**
* Convert the jumpto using a string to an integer value.
* The jumpto can contain a page name or one of our predefined values.
*
* @param int $lessonid the lesson id.
* @param array|null $jumptolist list of jumpto to treat.
* @return array|null list of jumpto already treated.
* @throws coding_exception
*/
protected function convert_page_jumpto(int $lessonid, ?array $jumptolist): ?array {
global $DB;
if (empty($jumptolist)) {
return $jumptolist;
}
foreach ($jumptolist as $i => $jumpto) {
if (empty($jumpto) || is_numeric($jumpto)) {
continue;
}
if (isset($this->jumptomap[$jumpto])) {
$jumptolist[$i] = $this->jumptomap[$jumpto];
continue;
}
$page = $DB->get_record('lesson_pages', ['lessonid' => $lessonid, 'title' => $jumpto], 'id');
if ($page === false) {
throw new coding_exception("Jump '$jumpto' not found in pages.");
}
$jumptolist[$i] = $page->id;
}
return $jumptolist;
}
}
+752
View File
@@ -0,0 +1,752 @@
<?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/>.
namespace mod_lesson;
/**
* Genarator tests class for mod_lesson.
*
* @package mod_lesson
* @category test
* @copyright 2013 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \mod_lesson_generator
*/
class generator_test extends \advanced_testcase {
public function test_create_instance(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$this->assertFalse($DB->record_exists('lesson', array('course' => $course->id)));
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course));
$records = $DB->get_records('lesson', array('course' => $course->id), 'id');
$this->assertEquals(1, count($records));
$this->assertTrue(array_key_exists($lesson->id, $records));
$params = array('course' => $course->id, 'name' => 'Another lesson');
$lesson = $this->getDataGenerator()->create_module('lesson', $params);
$records = $DB->get_records('lesson', array('course' => $course->id), 'id');
$this->assertEquals(2, count($records));
$this->assertEquals('Another lesson', $records[$lesson->id]->name);
}
public function test_create_content(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course));
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$page1 = $lessongenerator->create_content($lesson);
$page2 = $lessongenerator->create_content($lesson, array('title' => 'Custom title'));
$records = $DB->get_records('lesson_pages', array('lessonid' => $lesson->id), 'id');
$this->assertEquals(2, count($records));
$this->assertEquals($page1->id, $records[$page1->id]->id);
$this->assertEquals($page2->id, $records[$page2->id]->id);
$this->assertEquals('Custom title', $records[$page2->id]->title);
}
/**
* This tests the true/false question generator.
*/
public function test_create_question_truefalse(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course));
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$page1 = $lessongenerator->create_question_truefalse($lesson);
$page2 = $lessongenerator->create_question_truefalse($lesson, array('title' => 'Custom title'));
$records = $DB->get_records('lesson_pages', array('lessonid' => $lesson->id), 'id');
$p1answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page1->id), 'id');
$p2answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page2->id), 'id');
$this->assertCount(2, $records);
$this->assertCount(2, $p1answers); // True/false only supports 2 answer records.
$this->assertCount(2, $p2answers);
$this->assertEquals($page1->id, $records[$page1->id]->id);
$this->assertEquals($page2->id, $records[$page2->id]->id);
$this->assertEquals($page2->title, $records[$page2->id]->title);
}
/**
* This tests the multichoice question generator.
*/
public function test_create_question_multichoice(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course));
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$page1 = $lessongenerator->create_question_multichoice($lesson);
$page2 = $lessongenerator->create_question_multichoice($lesson, array('title' => 'Custom title'));
$records = $DB->get_records('lesson_pages', array('lessonid' => $lesson->id), 'id');
$p1answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page1->id), 'id');
$p2answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page2->id), 'id');
$this->assertCount(2, $records);
$this->assertCount(2, $p1answers); // Multichoice requires at least 2 records.
$this->assertCount(2, $p2answers);
$this->assertEquals($page1->id, $records[$page1->id]->id);
$this->assertEquals($page2->id, $records[$page2->id]->id);
$this->assertEquals($page2->title, $records[$page2->id]->title);
}
/**
* This tests the essay question generator.
*/
public function test_create_question_essay(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course));
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$page1 = $lessongenerator->create_question_essay($lesson);
$page2 = $lessongenerator->create_question_essay($lesson, array('title' => 'Custom title'));
$records = $DB->get_records('lesson_pages', array('lessonid' => $lesson->id), 'id');
$p1answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page1->id), 'id');
$p2answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page2->id), 'id');
$this->assertCount(2, $records);
$this->assertCount(1, $p1answers); // Essay creates a single (empty) answer record.
$this->assertCount(1, $p2answers);
$this->assertEquals($page1->id, $records[$page1->id]->id);
$this->assertEquals($page2->id, $records[$page2->id]->id);
$this->assertEquals($page2->title, $records[$page2->id]->title);
}
/**
* This tests the matching question generator.
*/
public function test_create_question_matching(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course));
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$page1 = $lessongenerator->create_question_matching($lesson);
$page2 = $lessongenerator->create_question_matching($lesson, array('title' => 'Custom title'));
$records = $DB->get_records('lesson_pages', array('lessonid' => $lesson->id), 'id');
$p1answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page1->id), 'id');
$p2answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page2->id), 'id');
$this->assertCount(2, $records);
$this->assertCount(4, $p1answers); // Matching creates two extra records plus 1 for each answer value.
$this->assertCount(4, $p2answers);
$this->assertEquals($page1->id, $records[$page1->id]->id);
$this->assertEquals($page2->id, $records[$page2->id]->id);
$this->assertEquals($page2->title, $records[$page2->id]->title);
}
/**
* This tests the numeric question generator.
*/
public function test_create_question_numeric(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course));
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$page1 = $lessongenerator->create_question_numeric($lesson);
$page2 = $lessongenerator->create_question_numeric($lesson, array('title' => 'Custom title'));
$records = $DB->get_records('lesson_pages', array('lessonid' => $lesson->id), 'id');
$p1answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page1->id), 'id');
$p2answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page2->id), 'id');
$this->assertCount(2, $records);
$this->assertCount(1, $p1answers); // Numeric only requires 1 answer.
$this->assertCount(1, $p2answers);
$this->assertEquals($page1->id, $records[$page1->id]->id);
$this->assertEquals($page2->id, $records[$page2->id]->id);
$this->assertEquals($page2->title, $records[$page2->id]->title);
}
/**
* This tests the shortanswer question generator.
*/
public function test_create_question_shortanswer(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course));
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$page1 = $lessongenerator->create_question_shortanswer($lesson);
$page2 = $lessongenerator->create_question_shortanswer($lesson, array('title' => 'Custom title'));
$records = $DB->get_records('lesson_pages', array('lessonid' => $lesson->id), 'id');
$p1answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page1->id), 'id');
$p2answers = $DB->get_records('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page2->id), 'id');
$this->assertCount(2, $records);
$this->assertCount(1, $p1answers); // Shortanswer only requires 1 answer.
$this->assertCount(1, $p2answers);
$this->assertEquals($page1->id, $records[$page1->id]->id);
$this->assertEquals($page2->id, $records[$page2->id]->id);
$this->assertEquals($page2->title, $records[$page2->id]->title);
}
/**
* This tests the generators for cluster, endofcluster and endofbranch pages.
*
* @covers ::create_cluster
* @covers ::create_endofcluster
* @covers ::create_endofbranch
* @dataProvider create_cluster_pages_provider
*
* @param string $type Type of page to test: LESSON_PAGE_CLUSTER, LESSON_PAGE_ENDOFCLUSTER or LESSON_PAGE_ENDOFBRANCH.
*/
public function test_create_cluster_pages(string $type): void {
global $CFG, $DB;
require_once($CFG->dirroot . '/mod/lesson/locallib.php');
require_once($CFG->dirroot . '/mod/lesson/pagetypes/cluster.php');
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', ['course' => $course]);
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$page2data = [
'title' => 'Custom title',
'contents_editor' => [
'text' => 'Custom content',
'format' => FORMAT_MOODLE,
'itemid' => 0,
],
'jumpto' => [LESSON_EOL],
];
switch ($type) {
case LESSON_PAGE_CLUSTER:
$page1 = $lessongenerator->create_cluster($lesson);
$page2 = $lessongenerator->create_cluster($lesson, $page2data);
break;
case LESSON_PAGE_ENDOFCLUSTER:
$page1 = $lessongenerator->create_endofcluster($lesson);
$page2 = $lessongenerator->create_endofcluster($lesson, $page2data);
break;
case LESSON_PAGE_ENDOFBRANCH:
$page1 = $lessongenerator->create_endofbranch($lesson);
$page2 = $lessongenerator->create_endofbranch($lesson, $page2data);
break;
default:
throw new coding_exception('Cluster page type not valid: ' . $type);
}
$records = $DB->get_records('lesson_pages', ['lessonid' => $lesson->id], 'id');
$p1answers = $DB->get_records('lesson_answers', ['lessonid' => $lesson->id, 'pageid' => $page1->id], 'id');
$p2answers = $DB->get_records('lesson_answers', ['lessonid' => $lesson->id, 'pageid' => $page2->id], 'id');
$this->assertCount(2, $records);
$this->assertEquals($page1->id, $records[$page1->id]->id);
$this->assertEquals($type, $records[$page1->id]->qtype);
$this->assertEquals($page2->id, $records[$page2->id]->id);
$this->assertEquals($type, $records[$page2->id]->qtype);
$this->assertEquals($page2->title, $records[$page2->id]->title);
$this->assertEquals($page2data['contents_editor']['text'], $records[$page2->id]->contents);
$this->assertCount(1, $p1answers);
$this->assertCount(1, $p2answers);
$this->assertEquals(LESSON_THISPAGE, array_pop($p1answers)->jumpto);
$this->assertEquals(LESSON_EOL, array_pop($p2answers)->jumpto);
}
/**
* Data provider for test_create_cluster_pages().
*
* @return array
*/
public static function create_cluster_pages_provider(): array {
// Using the page constants here throws an error: Undefined constant "mod_lesson\LESSON_PAGE_CLUSTER".
return [
'Cluster' => [
'type' => '30',
],
'End of cluster' => [
'type' => '31',
],
'End of branch' => [
'type' => '21',
],
];
}
/**
* Test create some pages and their answers.
*
* @covers ::create_page
* @covers ::create_answer
* @covers ::finish_generate_answer
*/
public function test_create_page_and_answers(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', ['course' => $course]);
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
// Define the pages. Only a couple pages will be created since each page type has their own unit tests.
$contentpage = [
'title' => 'First page name',
'content' => 'First page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
];
$multichoicepage = [
'title' => 'Multichoice question',
'content' => 'What animal is an amphibian?',
'qtype' => 'multichoice',
'lessonid' => $lesson->id,
];
$lessongenerator->create_page($contentpage);
$lessongenerator->create_page($multichoicepage);
// Check that pages haven't been generated yet because no answers were added.
$pages = $DB->get_records('lesson_pages', ['lessonid' => $lesson->id], 'id');
$this->assertEquals(0, count($pages));
// Now add answers to the pages.
$contentpagecontinueanswer = [
'page' => $contentpage['title'],
'answer' => 'Continue',
'jumpto' => 'Next page',
'score' => 1,
];
$contentpagestayanswer = [
'page' => $contentpage['title'],
'answer' => 'Stay',
'jumpto' => 'This page',
'score' => 0,
];
$multichoicepagefroganswer = [
'page' => $multichoicepage['title'],
'answer' => 'Frog',
'response' => 'Correct answer',
'jumpto' => 'Next page',
'score' => 1,
];
$multichoicepagecatanswer = [
'page' => $multichoicepage['title'],
'answer' => 'Cat',
'response' => 'Incorrect answer',
'jumpto' => 'This page',
'score' => 0,
];
$multichoicepagedoganswer = [
'page' => $multichoicepage['title'],
'answer' => 'Dog',
'response' => 'Incorrect answer',
'jumpto' => 'This page',
'score' => 0,
];
$lessongenerator->create_answer($contentpagecontinueanswer);
$lessongenerator->create_answer($contentpagestayanswer);
$lessongenerator->create_answer($multichoicepagefroganswer);
$lessongenerator->create_answer($multichoicepagecatanswer);
$lessongenerator->create_answer($multichoicepagedoganswer);
// Check that pages and answers haven't been generated yet because maybe not all answers have been added yet.
$pages = $DB->get_records('lesson_pages', ['lessonid' => $lesson->id], 'id');
$answers = $DB->get_records('lesson_answers', ['lessonid' => $lesson->id], 'id');
$this->assertEquals(0, count($pages));
$this->assertEquals(0, count($answers));
// Notify that all answers have been added, so pages can be created.
$lessongenerator->finish_generate_answer();
// Check that pages and answers have been created.
$pages = $DB->get_records('lesson_pages', ['lessonid' => $lesson->id], $DB->sql_order_by_text('title') . ' DESC');
$this->assertEquals(2, count($pages));
$contentpagedb = array_pop($pages);
$multichoicepagedb = array_pop($pages);
$this->assertEquals($contentpage['title'], $contentpagedb->title);
$this->assertEquals($contentpage['content'], $contentpagedb->contents);
$this->assertEquals(LESSON_PAGE_BRANCHTABLE, $contentpagedb->qtype);
$this->assertEquals($multichoicepage['title'], $multichoicepagedb->title);
$this->assertEquals($multichoicepage['content'], $multichoicepagedb->contents);
$this->assertEquals(LESSON_PAGE_MULTICHOICE, $multichoicepagedb->qtype);
$answers = $DB->get_records('lesson_answers', ['lessonid' => $lesson->id], $DB->sql_order_by_text('answer') . ' DESC');
$this->assertEquals(5, count($answers));
$multichoicepagecatanswerdb = array_pop($answers);
$contentpagecontinueanswerdb = array_pop($answers);
$multichoicepagedoganswerdb = array_pop($answers);
$multichoicepagefroganswerdb = array_pop($answers);
$contentpagestayanswerdb = array_pop($answers);
$this->assertEquals($contentpagedb->id, $contentpagecontinueanswerdb->pageid);
$this->assertEquals($contentpagecontinueanswer['answer'], $contentpagecontinueanswerdb->answer);
$this->assertEquals(LESSON_NEXTPAGE, $contentpagecontinueanswerdb->jumpto);
$this->assertEquals($contentpagecontinueanswer['score'], $contentpagecontinueanswerdb->score);
$this->assertEquals($contentpagedb->id, $contentpagestayanswerdb->pageid);
$this->assertEquals($contentpagestayanswer['answer'], $contentpagestayanswerdb->answer);
$this->assertEquals(LESSON_THISPAGE, $contentpagestayanswerdb->jumpto);
$this->assertEquals($contentpagestayanswer['score'], $contentpagestayanswerdb->score);
$this->assertEquals($multichoicepagedb->id, $multichoicepagefroganswerdb->pageid);
$this->assertEquals($multichoicepagefroganswer['answer'], $multichoicepagefroganswerdb->answer);
$this->assertEquals($multichoicepagefroganswer['response'], $multichoicepagefroganswerdb->response);
$this->assertEquals(LESSON_NEXTPAGE, $multichoicepagefroganswerdb->jumpto);
$this->assertEquals($multichoicepagefroganswer['score'], $multichoicepagefroganswerdb->score);
$this->assertEquals($multichoicepagedb->id, $multichoicepagedoganswerdb->pageid);
$this->assertEquals($multichoicepagedoganswer['answer'], $multichoicepagedoganswerdb->answer);
$this->assertEquals($multichoicepagedoganswer['response'], $multichoicepagedoganswerdb->response);
$this->assertEquals(LESSON_THISPAGE, $multichoicepagedoganswerdb->jumpto);
$this->assertEquals($multichoicepagedoganswer['score'], $multichoicepagedoganswerdb->score);
$this->assertEquals($multichoicepagedb->id, $multichoicepagecatanswerdb->pageid);
$this->assertEquals($multichoicepagecatanswer['answer'], $multichoicepagecatanswerdb->answer);
$this->assertEquals($multichoicepagecatanswer['response'], $multichoicepagecatanswerdb->response);
$this->assertEquals(LESSON_THISPAGE, $multichoicepagecatanswerdb->jumpto);
$this->assertEquals($multichoicepagecatanswer['score'], $multichoicepagecatanswerdb->score);
}
/**
* Test creating pages defining the previous pages.
*
* @covers ::create_page
*/
public function test_create_page_with_previouspage(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', ['course' => $course]);
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$firstpage = [
'title' => 'First page name',
'content' => 'First page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
'previouspage' => 0, // No previous page, this will be the first page.
];
$secondpage = [
'title' => 'Second page name',
'content' => 'Second page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
'previouspage' => 'First page name',
];
$thirdpage = [
'title' => 'Third page name',
'content' => 'Third page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
'previouspage' => 'Second page name',
];
// Create the third page first to check that the added order is not important, the order will still be calculated right.
$lessongenerator->create_page($thirdpage);
$lessongenerator->create_page($firstpage);
$lessongenerator->create_page($secondpage);
// Don't define any answers, the default answers will be added.
$lessongenerator->finish_generate_answer();
$pages = $DB->get_records('lesson_pages', ['lessonid' => $lesson->id], $DB->sql_order_by_text('title') . ' DESC');
$this->assertEquals(3, count($pages));
$firstpagedb = array_pop($pages);
$secondpagedb = array_pop($pages);
$thirdpagedb = array_pop($pages);
$this->assertEquals($firstpage['title'], $firstpagedb->title);
$this->assertEquals(0, $firstpagedb->prevpageid);
$this->assertEquals($secondpagedb->id, $firstpagedb->nextpageid);
$this->assertEquals($secondpage['title'], $secondpagedb->title);
$this->assertEquals($firstpagedb->id, $secondpagedb->prevpageid);
$this->assertEquals($thirdpagedb->id, $secondpagedb->nextpageid);
$this->assertEquals($thirdpage['title'], $thirdpagedb->title);
$this->assertEquals($secondpagedb->id, $thirdpagedb->prevpageid);
$this->assertEquals(0, $thirdpagedb->nextpageid);
}
/**
* Test creating a page with a previous page that doesn't exist.
*
* @covers ::create_page
*/
public function test_create_page_invalid_previouspage(): void {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', ['course' => $course]);
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$this->expectException('coding_exception');
$lessongenerator->create_page([
'title' => 'First page name',
'content' => 'First page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
'previouspage' => 'Invalid page',
]);
$lessongenerator->finish_generate_answer();
}
/**
* Test that circular dependencies are not allowed in previous pages.
*
* @covers ::create_page
*/
public function test_create_page_previouspage_circular_dependency(): void {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', ['course' => $course]);
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$this->expectException('coding_exception');
$lessongenerator->create_page([
'title' => 'First page name',
'content' => 'First page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
'previouspage' => 'Second page name',
]);
$lessongenerator->create_page([
'title' => 'Second page name',
'content' => 'Second page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
'previouspage' => 'First page name',
]);
$lessongenerator->finish_generate_answer();
}
/**
* Test creating an answer in a page that doesn't exist.
*
* @covers ::create_answer
*/
public function test_create_answer_invalid_page(): void {
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$this->expectException('coding_exception');
$lessongenerator->create_answer([
'page' => 'Invalid page',
]);
}
/**
* Test that all the possible values of jumpto work as expected when creating an answer.
*
* @covers ::create_answer
*/
public function test_create_answer_jumpto(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', ['course' => $course]);
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$contentpage = [
'title' => 'First page name',
'content' => 'First page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
];
$secondcontentpage = [
'title' => 'Second page name',
'content' => 'Second page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
];
$thirdcontentpage = [
'title' => 'Third page name',
'content' => 'Third page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
];
$lessongenerator->create_page($contentpage);
$lessongenerator->create_page($secondcontentpage);
$lessongenerator->create_page($thirdcontentpage);
$lessongenerator->create_answer([
'page' => $contentpage['title'],
'answer' => 'A',
'jumpto' => 'This page',
]);
$lessongenerator->create_answer([
'page' => $contentpage['title'],
'answer' => 'B',
'jumpto' => 'Next page',
]);
$lessongenerator->create_answer([
'page' => $contentpage['title'],
'answer' => 'C',
'jumpto' => 'Previous page',
]);
$lessongenerator->create_answer([
'page' => $secondcontentpage['title'],
'answer' => 'D',
'jumpto' => 'End of lesson',
]);
$lessongenerator->create_answer([
'page' => $secondcontentpage['title'],
'answer' => 'E',
'jumpto' => 'Unseen question within a content page',
]);
$lessongenerator->create_answer([
'page' => $secondcontentpage['title'],
'answer' => 'F',
'jumpto' => 'Random question within a content page',
]);
$lessongenerator->create_answer([
'page' => $thirdcontentpage['title'],
'answer' => 'G',
'jumpto' => 'Random content page',
]);
$lessongenerator->create_answer([
'page' => $thirdcontentpage['title'],
'answer' => 'H',
'jumpto' => 'Unseen question within a cluster',
]);
$lessongenerator->create_answer([
'page' => $thirdcontentpage['title'],
'answer' => 'I',
'jumpto' => 1234, // A page ID, it doesn't matter that it doesn't exist.
]);
$lessongenerator->create_answer([
'page' => $contentpage['title'],
'answer' => 'J',
'jumpto' => 'Third page name',
]);
$lessongenerator->create_answer([
'page' => $thirdcontentpage['title'],
'answer' => 'K',
'jumpto' => 'Second page name',
]);
$lessongenerator->finish_generate_answer();
$secondcontentpagedb = $DB->get_record('lesson_pages', ['lessonid' => $lesson->id, 'title' => $secondcontentpage['title']]);
$thirdcontentpagedb = $DB->get_record('lesson_pages', ['lessonid' => $lesson->id, 'title' => $thirdcontentpage['title']]);
$answers = $DB->get_records('lesson_answers', ['lessonid' => $lesson->id], $DB->sql_order_by_text('answer') . ' DESC');
$this->assertEquals(11, count($answers));
$this->assertEquals(LESSON_THISPAGE, array_pop($answers)->jumpto);
$this->assertEquals(LESSON_NEXTPAGE, array_pop($answers)->jumpto);
$this->assertEquals(LESSON_PREVIOUSPAGE, array_pop($answers)->jumpto);
$this->assertEquals(LESSON_EOL, array_pop($answers)->jumpto);
$this->assertEquals(LESSON_UNSEENBRANCHPAGE, array_pop($answers)->jumpto);
$this->assertEquals(LESSON_RANDOMPAGE, array_pop($answers)->jumpto);
$this->assertEquals(LESSON_RANDOMBRANCH, array_pop($answers)->jumpto);
$this->assertEquals(LESSON_CLUSTERJUMP, array_pop($answers)->jumpto);
$this->assertEquals(1234, array_pop($answers)->jumpto);
$this->assertEquals($thirdcontentpagedb->id, array_pop($answers)->jumpto);
$this->assertEquals($secondcontentpagedb->id, array_pop($answers)->jumpto);
}
/**
* Test invalid jumpto when creating answers.
*
* @covers ::create_answer
*/
public function test_create_answer_invalid_jumpto(): void {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', ['course' => $course]);
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$contentpage = [
'title' => 'First page name',
'content' => 'First page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
];
$lessongenerator->create_page($contentpage);
$lessongenerator->create_answer([
'page' => $contentpage['title'],
'answer' => 'Next',
'jumpto' => 'Invalid page',
]);
$this->expectException('coding_exception');
$lessongenerator->finish_generate_answer();
}
/**
* Test that circular dependencies are not allowed when creating answers.
*
* @covers ::create_answer
*/
public function test_create_answer_jumpto_circular_dependency(): void {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', ['course' => $course]);
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$contentpage = [
'title' => 'First page name',
'content' => 'First page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
];
$secondcontentpage = [
'title' => 'Second page name',
'content' => 'Second page contents',
'qtype' => 'content',
'lessonid' => $lesson->id,
];
$lessongenerator->create_page($contentpage);
$lessongenerator->create_page($secondcontentpage);
$lessongenerator->create_answer([
'page' => $contentpage['title'],
'answer' => 'Next',
'jumpto' => 'Second page name',
]);
$lessongenerator->create_answer([
'page' => $contentpage['title'],
'answer' => 'Back',
'jumpto' => 'First page name',
]);
$this->expectException('coding_exception');
$lessongenerator->finish_generate_answer();
}
}
File diff suppressed because it is too large Load Diff
+289
View File
@@ -0,0 +1,289 @@
<?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/>.
/**
* locallib tests.
*
* @package mod_lesson
* @category test
* @copyright 2016 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson;
use lesson;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
/**
* locallib testcase.
*
* @package mod_lesson
* @category test
* @copyright 2016 Adrian Greeve <adrian@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class locallib_test extends \advanced_testcase {
/**
* Test duplicating a lesson page element.
*/
public function test_duplicate_page(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lessonmodule = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
// Convert to a lesson object.
$lesson = new lesson($lessonmodule);
// Set up a generator to create content.
$generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
$tfrecord = $generator->create_question_truefalse($lesson);
$lesson->duplicate_page($tfrecord->id);
// Lesson pages.
$records = $DB->get_records('lesson_pages', array('qtype' => 2));
$sameelements = array('lessonid', 'qtype', 'qoption', 'layout', 'display', 'title', 'contents', 'contentsformat');
$baserecord = array_shift($records);
$secondrecord = array_shift($records);
foreach ($sameelements as $element) {
$this->assertEquals($baserecord->$element, $secondrecord->$element);
}
// Need lesson answers as well.
$baserecordanswers = array_values($DB->get_records('lesson_answers', array('pageid' => $baserecord->id)));
$secondrecordanswers = array_values($DB->get_records('lesson_answers', array('pageid' => $secondrecord->id)));
$sameanswerelements = array('lessonid', 'jumpto', 'grade', 'score', 'flags', 'answer', 'answerformat', 'response',
'responseformat');
foreach ($baserecordanswers as $key => $baseanswer) {
foreach ($sameanswerelements as $element) {
$this->assertEquals($baseanswer->$element, $secondrecordanswers[$key]->$element);
}
}
}
/**
* Test test_lesson_get_user_deadline().
*/
public function test_lesson_get_user_deadline(): void {
global $DB;
$this->resetAfterTest();
$this->setAdminUser();
$basetimestamp = time(); // The timestamp we will base the enddates on.
// Create generator, course and lessons.
$student1 = $this->getDataGenerator()->create_user();
$student2 = $this->getDataGenerator()->create_user();
$student3 = $this->getDataGenerator()->create_user();
$teacher = $this->getDataGenerator()->create_user();
$course = $this->getDataGenerator()->create_course();
$lessongenerator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
// Both lessons close in two hours.
$lesson1 = $lessongenerator->create_instance(array('course' => $course->id, 'deadline' => $basetimestamp + 7200));
$lesson2 = $lessongenerator->create_instance(array('course' => $course->id, 'deadline' => $basetimestamp + 7200));
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$student1id = $student1->id;
$student2id = $student2->id;
$student3id = $student3->id;
$teacherid = $teacher->id;
// Users enrolments.
$studentrole = $DB->get_record('role', array('shortname' => 'student'));
$teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
$this->getDataGenerator()->enrol_user($student1id, $course->id, $studentrole->id, 'manual');
$this->getDataGenerator()->enrol_user($student2id, $course->id, $studentrole->id, 'manual');
$this->getDataGenerator()->enrol_user($student3id, $course->id, $studentrole->id, 'manual');
$this->getDataGenerator()->enrol_user($teacherid, $course->id, $teacherrole->id, 'manual');
// Create groups.
$group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
$group1id = $group1->id;
$group2id = $group2->id;
$this->getDataGenerator()->create_group_member(array('userid' => $student1id, 'groupid' => $group1id));
$this->getDataGenerator()->create_group_member(array('userid' => $student2id, 'groupid' => $group2id));
// Group 1 gets an group override for lesson 1 to close in three hours.
$record1 = (object) [
'lessonid' => $lesson1->id,
'groupid' => $group1id,
'deadline' => $basetimestamp + 10800 // In three hours.
];
$DB->insert_record('lesson_overrides', $record1);
// Let's test lesson 1 closes in three hours for user student 1 since member of group 1.
// lesson 2 closes in two hours.
$this->setUser($student1id);
$params = new \stdClass();
$comparearray = array();
$object = new \stdClass();
$object->id = $lesson1->id;
$object->userdeadline = $basetimestamp + 10800; // The overriden deadline for lesson 1.
$comparearray[$lesson1->id] = $object;
$object = new \stdClass();
$object->id = $lesson2->id;
$object->userdeadline = $basetimestamp + 7200; // The unchanged deadline for lesson 2.
$comparearray[$lesson2->id] = $object;
$this->assertEquals($comparearray, lesson_get_user_deadline($course->id));
// Let's test lesson 1 closes in two hours (the original value) for user student 3 since member of no group.
$this->setUser($student3id);
$params = new \stdClass();
$comparearray = array();
$object = new \stdClass();
$object->id = $lesson1->id;
$object->userdeadline = $basetimestamp + 7200; // The original deadline for lesson 1.
$comparearray[$lesson1->id] = $object;
$object = new \stdClass();
$object->id = $lesson2->id;
$object->userdeadline = $basetimestamp + 7200; // The original deadline for lesson 2.
$comparearray[$lesson2->id] = $object;
$this->assertEquals($comparearray, lesson_get_user_deadline($course->id));
// User 2 gets an user override for lesson 1 to close in four hours.
$record2 = (object) [
'lessonid' => $lesson1->id,
'userid' => $student2id,
'deadline' => $basetimestamp + 14400 // In four hours.
];
$DB->insert_record('lesson_overrides', $record2);
// Let's test lesson 1 closes in four hours for user student 2 since personally overriden.
// lesson 2 closes in two hours.
$this->setUser($student2id);
$comparearray = array();
$object = new \stdClass();
$object->id = $lesson1->id;
$object->userdeadline = $basetimestamp + 14400; // The overriden deadline for lesson 1.
$comparearray[$lesson1->id] = $object;
$object = new \stdClass();
$object->id = $lesson2->id;
$object->userdeadline = $basetimestamp + 7200; // The unchanged deadline for lesson 2.
$comparearray[$lesson2->id] = $object;
$this->assertEquals($comparearray, lesson_get_user_deadline($course->id));
// Let's test a teacher sees the original times.
// lesson 1 and lesson 2 close in two hours.
$this->setUser($teacherid);
$comparearray = array();
$object = new \stdClass();
$object->id = $lesson1->id;
$object->userdeadline = $basetimestamp + 7200; // The unchanged deadline for lesson 1.
$comparearray[$lesson1->id] = $object;
$object = new \stdClass();
$object->id = $lesson2->id;
$object->userdeadline = $basetimestamp + 7200; // The unchanged deadline for lesson 2.
$comparearray[$lesson2->id] = $object;
$this->assertEquals($comparearray, lesson_get_user_deadline($course->id));
}
public function test_is_participant(): void {
global $USER, $DB;
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$student = $this->getDataGenerator()->create_and_enrol($course, 'student');
$student2 = $this->getDataGenerator()->create_and_enrol($course, 'student', [], 'manual', 0, 0, ENROL_USER_SUSPENDED);
$lessonmodule = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
// Login as student.
$this->setUser($student);
// Convert to a lesson object.
$lesson = new lesson($lessonmodule);
$this->assertEquals(true, $lesson->is_participant($student->id),
'Student is enrolled, active and can participate');
// Login as student2.
$this->setUser($student2);
$this->assertEquals(false, $lesson->is_participant($student2->id),
'Student is enrolled, suspended and can NOT participate');
// Login as an admin.
$this->setAdminUser();
$this->assertEquals(false, $lesson->is_participant($USER->id),
'Admin is not enrolled and can NOT participate');
$this->getDataGenerator()->enrol_user(2, $course->id);
$this->assertEquals(true, $lesson->is_participant($USER->id),
'Admin is enrolled and can participate');
$this->getDataGenerator()->enrol_user(2, $course->id, [], 'manual', 0, 0, ENROL_USER_SUSPENDED);
$this->assertEquals(true, $lesson->is_participant($USER->id),
'Admin is enrolled, suspended and can participate');
}
/**
* Data provider for test_get_last_attempt.
*
* @return array
*/
public function get_last_attempt_dataprovider() {
return [
[0, [(object)['id' => 1], (object)['id' => 2], (object)['id' => 3]], (object)['id' => 3]],
[1, [(object)['id' => 1], (object)['id' => 2], (object)['id' => 3]], (object)['id' => 1]],
[2, [(object)['id' => 1], (object)['id' => 2], (object)['id' => 3]], (object)['id' => 2]],
[3, [(object)['id' => 1], (object)['id' => 2], (object)['id' => 3]], (object)['id' => 3]],
[4, [(object)['id' => 1], (object)['id' => 2], (object)['id' => 3]], (object)['id' => 3]],
];
}
/**
* Test the get_last_attempt() method.
*
* @dataProvider get_last_attempt_dataprovider
* @param int $maxattempts Lesson setting.
* @param array $attempts The list of student attempts.
* @param object $expected Expected result.
*/
public function test_get_last_attempt($maxattempts, $attempts, $expected): void {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$lesson = $this->getDataGenerator()->create_module('lesson', ['course' => $course, 'maxattempts' => $maxattempts]);
$lesson = new lesson($lesson);
$this->assertEquals($expected, $lesson->get_last_attempt($attempts));
}
}
+160
View File
@@ -0,0 +1,160 @@
<?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/>.
namespace mod_lesson;
use mod_lesson\local\numeric\helper;
/**
* This class contains the test cases for the numeric helper functions
*
* @package mod_lesson
* @category test
* @copyright 2020 Peter Dias
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
class numeric_helper_test extends \advanced_testcase {
/**
* Test the lesson_unformat_numeric_value function.
*
* @dataProvider lesson_unformat_dataprovider
* @param $decsep
* @param $tests
*/
public function test_lesson_unformat_numeric_value($decsep, $tests): void {
$this->define_local_decimal_separator($decsep);
foreach ($tests as $test) {
$this->assertEquals($test[1], helper::lesson_unformat_numeric_value($test[0]));
}
}
/**
* Test the lesson_format_numeric_value function.
*
* @dataProvider lesson_format_dataprovider
* @param $decsep
* @param $tests
*/
public function test_lesson_format_numeric_value($decsep, $tests): void {
$this->define_local_decimal_separator($decsep);
foreach ($tests as $test) {
$this->assertEquals($test[1], helper::lesson_format_numeric_value($test[0]));
}
}
/**
* Provide various cases for the unformat test function
*
* @return array
*/
public function lesson_unformat_dataprovider() {
return [
"Using a decimal as a separator" => [
"decsep" => ".",
"test" => [
["2.1", 2.1],
["1:4.2", "1:4.2"],
["2,1", 2],
["1:4,2", "1:4"],
["", null]
]
],
"Using a comma as a separator" => [
"decsep" => ",",
"test" => [
["2,1", 2.1],
["1:4,2", "1:4.2"],
["2.1", 2.1],
["1:4.2", "1:4.2"],
]
],
"Using a X as a separator" => [
"decsep" => "X",
"test" => [
["2X1", 2.1],
["1:4X2", "1:4.2"],
["2.1", 2.1],
["1:4.2", "1:4.2"],
]
]
];
}
/**
* Provide various cases for the unformat test function
*
* @return array
*/
public function lesson_format_dataprovider() {
return [
"Using a decimal as a separator" => [
"decsep" => ".",
"test" => [
["2.1", 2.1],
["1:4.2", "1:4.2"],
["2,1", "2,1"],
["1:4,2", "1:4,2"]
]
],
"Using a comma as a separator" => [
"decsep" => ",",
"test" => [
["2,1", "2,1"],
["1:4,2", "1:4,2"],
["2.1", "2,1"],
[2.1, "2,1"],
["1:4.2", "1:4,2"],
]
],
"Using a X as a separator" => [
"decsep" => "X",
"test" => [
["2X1", "2X1"],
["1:4X2", "1:4X2"],
["2.1", "2X1"],
["1:4.2", "1:4X2"],
]
]
];
}
/**
* Define a local decimal separator.
*
* It is not possible to directly change the result of get_string in
* a unit test. Instead, we create a language pack for language 'xx' in
* dataroot and make langconfig.php with the string we need to change.
* The default example separator used here is 'X'.
*
* @param string $decsep Separator character. Defaults to `'X'`.
*/
protected function define_local_decimal_separator(string $decsep = 'X') {
global $SESSION, $CFG;
$SESSION->lang = 'xx';
$langconfig = "<?php\n\$string['decsep'] = '$decsep';";
$langfolder = $CFG->dataroot . '/lang/xx';
check_dir_exists($langfolder);
file_put_contents($langfolder . '/langconfig.php', $langconfig);
// Ensure the new value is picked up and not taken from the cache.
$stringmanager = get_string_manager();
$stringmanager->reset_caches(true);
}
}
+66
View File
@@ -0,0 +1,66 @@
<?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/>.
namespace mod_lesson;
use lesson_page_type_essay;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/mod/lesson/locallib.php');
require_once($CFG->dirroot . '/mod/lesson/pagetypes/essay.php');
/**
* This class contains the test cases for some of the functions in the lesson essay page type class.
*
* @package mod_lesson
* @category test
* @copyright 2015 Jean-Michel Vedrine
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
*/
class pagetypes_test extends \advanced_testcase {
public function test_lesson_essay_extract_useranswer(): void {
// Test that reponseformat is added when not present.
$answer = 'O:8:"stdClass":6:{s:4:"sent";i:1;s:6:"graded";i:1;s:5:"score";s:1:"1";'
. 's:6:"answer";s:64:"<p>This is my answer <b>with bold</b> and <i>italics</i><br></p>";'
. 's:12:"answerformat";s:1:"1";s:8:"response";s:10:"Well done!";}';
$userresponse = new \stdClass;
$userresponse->sent = 1;
$userresponse->graded = 1;
$userresponse->score = 1;
$userresponse->answer = "<p>This is my answer <b>with bold</b> and <i>italics</i><br></p>";
$userresponse->answerformat = FORMAT_HTML;
$userresponse->response = "Well done!";
$userresponse->responseformat = FORMAT_HTML;
$this->assertEquals($userresponse, lesson_page_type_essay::extract_useranswer($answer));
// Test that reponseformat is not modified when present.
$answer = 'O:8:"stdClass":7:{s:4:"sent";i:0;s:6:"graded";i:1;s:5:"score";s:1:"0";'
. 's:6:"answer";s:64:"<p>This is my answer <b>with bold</b> and <i>italics</i><br></p>";'
. 's:12:"answerformat";s:1:"1";s:8:"response";s:10:"Well done!";s:14:"responseformat";s:1:"2";}';
$userresponse = new \stdClass;
$userresponse->sent = 0;
$userresponse->graded = 1;
$userresponse->score = 0;
$userresponse->answer = "<p>This is my answer <b>with bold</b> and <i>italics</i><br></p>";
$userresponse->answerformat = FORMAT_HTML;
$userresponse->response = "Well done!";
$userresponse->responseformat = FORMAT_PLAIN;
$this->assertEquals($userresponse, lesson_page_type_essay::extract_useranswer($answer));
}
}
+891
View File
@@ -0,0 +1,891 @@
<?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/>.
/**
* Data provider tests.
*
* @package mod_lesson
* @category test
* @copyright 2018 Frédéric Massart
* @author Frédéric Massart <fred@branchup.tech>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_lesson\privacy;
defined('MOODLE_INTERNAL') || die();
global $CFG;
use core_privacy\tests\provider_testcase;
use core_privacy\local\request\approved_contextlist;
use core_privacy\local\request\approved_userlist;
use core_privacy\local\request\transform;
use core_privacy\local\request\writer;
use mod_lesson\privacy\provider;
/**
* Data provider testcase class.
*
* @package mod_lesson
* @category test
* @copyright 2018 Frédéric Massart
* @author Frédéric Massart <fred@branchup.tech>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider_test extends provider_testcase {
public function setUp(): void {
global $PAGE;
$this->setAdminUser(); // The data generator complains without this.
$this->resetAfterTest();
$PAGE->get_renderer('core');
}
public function test_get_contexts_for_userid(): void {
$dg = $this->getDataGenerator();
$c1 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$u3 = $dg->create_user();
$u4 = $dg->create_user();
$u5 = $dg->create_user();
$u6 = $dg->create_user();
$cm1 = $dg->create_module('lesson', ['course' => $c1]);
$cm2 = $dg->create_module('lesson', ['course' => $c1]);
$cm3 = $dg->create_module('lesson', ['course' => $c1]);
$cm1ctx = \context_module::instance($cm1->cmid);
$cm2ctx = \context_module::instance($cm2->cmid);
$cm3ctx = \context_module::instance($cm3->cmid);
$this->create_attempt($cm1, $u1);
$this->create_grade($cm2, $u2);
$this->create_timer($cm3, $u3);
$this->create_branch($cm2, $u4);
$this->create_override($cm1, $u5);
$this->create_attempt($cm2, $u6);
$this->create_grade($cm2, $u6);
$this->create_timer($cm1, $u6);
$this->create_branch($cm2, $u6);
$this->create_override($cm3, $u6);
$contextids = provider::get_contexts_for_userid($u1->id)->get_contextids();
$this->assertCount(1, $contextids);
$this->assertTrue(in_array($cm1ctx->id, $contextids));
$contextids = provider::get_contexts_for_userid($u2->id)->get_contextids();
$this->assertCount(1, $contextids);
$this->assertTrue(in_array($cm2ctx->id, $contextids));
$contextids = provider::get_contexts_for_userid($u3->id)->get_contextids();
$this->assertCount(1, $contextids);
$this->assertTrue(in_array($cm3ctx->id, $contextids));
$contextids = provider::get_contexts_for_userid($u4->id)->get_contextids();
$this->assertCount(1, $contextids);
$this->assertTrue(in_array($cm2ctx->id, $contextids));
$contextids = provider::get_contexts_for_userid($u5->id)->get_contextids();
$this->assertCount(1, $contextids);
$this->assertTrue(in_array($cm1ctx->id, $contextids));
$contextids = provider::get_contexts_for_userid($u6->id)->get_contextids();
$this->assertCount(3, $contextids);
$this->assertTrue(in_array($cm1ctx->id, $contextids));
$this->assertTrue(in_array($cm2ctx->id, $contextids));
$this->assertTrue(in_array($cm3ctx->id, $contextids));
}
/*
* Test for provider::get_users_in_context().
*/
public function test_get_users_in_context(): void {
$dg = $this->getDataGenerator();
$c1 = $dg->create_course();
$component = 'mod_lesson';
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$u3 = $dg->create_user();
$u4 = $dg->create_user();
$u5 = $dg->create_user();
$u6 = $dg->create_user();
$cm1 = $dg->create_module('lesson', ['course' => $c1]);
$cm2 = $dg->create_module('lesson', ['course' => $c1]);
$cm1ctx = \context_module::instance($cm1->cmid);
$cm2ctx = \context_module::instance($cm2->cmid);
$this->create_attempt($cm1, $u1);
$this->create_grade($cm1, $u2);
$this->create_timer($cm1, $u3);
$this->create_branch($cm1, $u4);
$this->create_override($cm1, $u5);
$this->create_attempt($cm2, $u6);
$this->create_grade($cm2, $u6);
$this->create_timer($cm2, $u6);
$this->create_branch($cm2, $u6);
$this->create_override($cm2, $u6);
$context = \context_module::instance($cm1->cmid);
$userlist = new \core_privacy\local\request\userlist($context, $component);
provider::get_users_in_context($userlist);
$userids = $userlist->get_userids();
$this->assertCount(5, $userids);
$expected = [$u1->id, $u2->id, $u3->id, $u4->id, $u5->id];
$actual = $userids;
sort($expected);
sort($actual);
$this->assertEquals($expected, $actual);
$context = \context_module::instance($cm2->cmid);
$userlist = new \core_privacy\local\request\userlist($context, $component);
provider::get_users_in_context($userlist);
$userids = $userlist->get_userids();
$this->assertCount(1, $userids);
$this->assertEquals([$u6->id], $userids);
}
public function test_delete_data_for_all_users_in_context(): void {
global $DB;
$dg = $this->getDataGenerator();
$c1 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$cm1 = $dg->create_module('lesson', ['course' => $c1]);
$cm2 = $dg->create_module('lesson', ['course' => $c1]);
$cm3 = $dg->create_module('lesson', ['course' => $c1]);
$c1ctx = \context_course::instance($c1->id);
$cm1ctx = \context_module::instance($cm1->cmid);
$cm2ctx = \context_module::instance($cm2->cmid);
$cm3ctx = \context_module::instance($cm3->cmid);
$this->create_attempt($cm1, $u1);
$this->create_grade($cm1, $u1);
$this->create_timer($cm1, $u1);
$this->create_branch($cm1, $u1);
$this->create_override($cm1, $u1);
$this->create_attempt($cm1, $u2);
$this->create_grade($cm1, $u2);
$this->create_timer($cm1, $u2);
$this->create_branch($cm1, $u2);
$this->create_override($cm1, $u2);
$this->create_attempt($cm2, $u1);
$this->create_grade($cm2, $u1);
$this->create_timer($cm2, $u1);
$this->create_branch($cm2, $u1);
$this->create_override($cm2, $u1);
$this->create_attempt($cm2, $u2);
$this->create_grade($cm2, $u2);
$this->create_timer($cm2, $u2);
$this->create_branch($cm2, $u2);
$this->create_override($cm2, $u2);
$assertcm1nochange = function() use ($DB, $u1, $u2, $cm1) {
$this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
};
$assertcm2nochange = function() use ($DB, $u1, $u2, $cm2) {
$this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u2->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u2->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u2->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u2->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u2->id, 'lessonid' => $cm2->id]));
};
// Confirm existing state.
$assertcm1nochange();
$assertcm2nochange();
// Delete the course: no change.
provider::delete_data_for_all_users_in_context(\context_course::instance($c1->id));
$assertcm1nochange();
$assertcm2nochange();
// Delete another module: no change.
provider::delete_data_for_all_users_in_context(\context_module::instance($cm3->cmid));
$assertcm1nochange();
$assertcm2nochange();
// Delete cm1: no change in cm2.
provider::delete_data_for_all_users_in_context(\context_module::instance($cm1->cmid));
$assertcm2nochange();
$this->assertFalse($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_attempts', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_grades', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_timer', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_branch', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_overrides', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
}
public function test_delete_data_for_user(): void {
global $DB;
$dg = $this->getDataGenerator();
$c1 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$cm1 = $dg->create_module('lesson', ['course' => $c1]);
$cm2 = $dg->create_module('lesson', ['course' => $c1]);
$cm3 = $dg->create_module('lesson', ['course' => $c1]);
$c1ctx = \context_course::instance($c1->id);
$cm1ctx = \context_module::instance($cm1->cmid);
$cm2ctx = \context_module::instance($cm2->cmid);
$cm3ctx = \context_module::instance($cm3->cmid);
$this->create_attempt($cm1, $u1);
$this->create_grade($cm1, $u1);
$this->create_timer($cm1, $u1);
$this->create_branch($cm1, $u1);
$this->create_override($cm1, $u1);
$this->create_attempt($cm1, $u2);
$this->create_grade($cm1, $u2);
$this->create_timer($cm1, $u2);
$this->create_branch($cm1, $u2);
$this->create_override($cm1, $u2);
$this->create_attempt($cm2, $u1);
$this->create_grade($cm2, $u1);
$this->create_timer($cm2, $u1);
$this->create_branch($cm2, $u1);
$this->create_override($cm2, $u1);
$this->create_attempt($cm2, $u2);
$this->create_grade($cm2, $u2);
$this->create_timer($cm2, $u2);
$this->create_branch($cm2, $u2);
$this->create_override($cm2, $u2);
$assertu1nochange = function() use ($DB, $u1, $cm1, $cm2) {
$this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
};
$assertu2nochange = function() use ($DB, $u2, $cm1, $cm2) {
$this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u2->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u2->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u2->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u2->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u2->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u2->id, 'lessonid' => $cm2->id]));
};
// Confirm existing state.
$assertu1nochange();
$assertu2nochange();
// Delete the course: no change.
provider::delete_data_for_user(new approved_contextlist($u1, 'mod_lesson', [\context_course::instance($c1->id)->id]));
$assertu1nochange();
$assertu2nochange();
// Delete another module: no change.
provider::delete_data_for_user(new approved_contextlist($u1, 'mod_lesson', [\context_module::instance($cm3->cmid)->id]));
$assertu1nochange();
$assertu2nochange();
// Delete u1 in cm1: no change for u2 and in cm2.
provider::delete_data_for_user(new approved_contextlist($u1, 'mod_lesson', [\context_module::instance($cm1->cmid)->id]));
$assertu2nochange();
$this->assertFalse($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertFalse($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm1->id]));
$this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $u1->id, 'lessonid' => $cm2->id]));
}
/*
* Test for provider::delete_data_for_users().
*/
public function test_delete_data_for_users(): void {
global $DB;
$dg = $this->getDataGenerator();
$c1 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$cm1 = $dg->create_module('lesson', ['course' => $c1]);
$cm2 = $dg->create_module('lesson', ['course' => $c1]);
$cm3 = $dg->create_module('lesson', ['course' => $c1]);
$context1 = \context_module::instance($cm1->cmid);
$context3 = \context_module::instance($cm3->cmid);
$this->create_attempt($cm1, $u1);
$this->create_grade($cm1, $u1);
$this->create_timer($cm1, $u1);
$this->create_branch($cm1, $u1);
$this->create_override($cm1, $u1);
$this->create_attempt($cm1, $u2);
$this->create_grade($cm1, $u2);
$this->create_timer($cm1, $u2);
$this->create_branch($cm1, $u2);
$this->create_override($cm1, $u2);
$this->create_attempt($cm2, $u1);
$this->create_grade($cm2, $u1);
$this->create_timer($cm2, $u1);
$this->create_branch($cm2, $u1);
$this->create_override($cm2, $u1);
$this->create_attempt($cm2, $u2);
$this->create_grade($cm2, $u2);
$this->create_timer($cm2, $u2);
$this->create_branch($cm2, $u2);
$this->create_override($cm2, $u2);
$assertnochange = function($user, $cm) use ($DB) {
$this->assertTrue($DB->record_exists('lesson_attempts', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertTrue($DB->record_exists('lesson_grades', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertTrue($DB->record_exists('lesson_timer', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertTrue($DB->record_exists('lesson_branch', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertTrue($DB->record_exists('lesson_overrides', ['userid' => $user->id, 'lessonid' => $cm->id]));
};
$assertdeleted = function($user, $cm) use ($DB) {
$this->assertFalse($DB->record_exists('lesson_attempts', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertFalse($DB->record_exists('lesson_grades', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertFalse($DB->record_exists('lesson_timer', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertFalse($DB->record_exists('lesson_branch', ['userid' => $user->id, 'lessonid' => $cm->id]));
$this->assertFalse($DB->record_exists('lesson_overrides', ['userid' => $user->id, 'lessonid' => $cm->id]));
};
// Confirm existing state.
$assertnochange($u1, $cm1);
$assertnochange($u1, $cm2);
$assertnochange($u2, $cm1);
$assertnochange($u2, $cm2);
// Delete another module: no change.
$approveduserlist = new approved_userlist($context3, 'mod_lesson', [$u1->id]);
provider::delete_data_for_users($approveduserlist);
$assertnochange($u1, $cm1);
$assertnochange($u1, $cm2);
$assertnochange($u2, $cm1);
$assertnochange($u2, $cm2);
// Delete cm1 for u1: no change for u2 and in cm2.
$approveduserlist = new approved_userlist($context1, 'mod_lesson', [$u1->id]);
provider::delete_data_for_users($approveduserlist);
$assertdeleted($u1, $cm1);
$assertnochange($u1, $cm2);
$assertnochange($u2, $cm1);
$assertnochange($u2, $cm2);
}
public function test_export_data_for_user_overrides(): void {
$dg = $this->getDataGenerator();
$c1 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$cm1 = $dg->create_module('lesson', ['course' => $c1]);
$cm2 = $dg->create_module('lesson', ['course' => $c1]);
$cm1ctx = \context_module::instance($cm1->cmid);
$cm2ctx = \context_module::instance($cm2->cmid);
$now = time();
$this->create_override($cm1, $u1); // All null.
$this->create_override($cm2, $u1, [
'available' => $now - 3600,
'deadline' => $now + 3600,
'timelimit' => 123,
'review' => 1,
'maxattempts' => 1,
'retake' => 0,
'password' => '1337 5p34k'
]);
$this->create_override($cm1, $u2, [
'available' => $now - 1230,
'timelimit' => 456,
'maxattempts' => 5,
'retake' => 1,
]);
provider::export_user_data(new approved_contextlist($u1, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id]));
$data = writer::with_context($cm1ctx)->get_data([]);
$this->assertNotEmpty($data);
$data = writer::with_context($cm1ctx)->get_related_data([], 'overrides');
$this->assertNull($data->available);
$this->assertNull($data->deadline);
$this->assertNull($data->timelimit);
$this->assertNull($data->review);
$this->assertNull($data->maxattempts);
$this->assertNull($data->retake);
$this->assertNull($data->password);
$data = writer::with_context($cm2ctx)->get_data([]);
$this->assertNotEmpty($data);
$data = writer::with_context($cm2ctx)->get_related_data([], 'overrides');
$this->assertEquals(transform::datetime($now - 3600), $data->available);
$this->assertEquals(transform::datetime($now + 3600), $data->deadline);
$this->assertEquals(format_time(123), $data->timelimit);
$this->assertEquals(transform::yesno(true), $data->review);
$this->assertEquals(1, $data->maxattempts);
$this->assertEquals(transform::yesno(false), $data->retake);
$this->assertEquals('1337 5p34k', $data->password);
writer::reset();
provider::export_user_data(new approved_contextlist($u2, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id]));
$data = writer::with_context($cm1ctx)->get_data([]);
$this->assertNotEmpty($data);
$data = writer::with_context($cm1ctx)->get_related_data([], 'overrides');
$this->assertEquals(transform::datetime($now - 1230), $data->available);
$this->assertNull($data->deadline);
$this->assertEquals(format_time(456), $data->timelimit);
$this->assertNull($data->review);
$this->assertEquals(5, $data->maxattempts);
$this->assertEquals(transform::yesno(true), $data->retake);
$this->assertNull($data->password);
$data = writer::with_context($cm2ctx)->get_data([]);
$this->assertNotEmpty($data);
$data = writer::with_context($cm2ctx)->get_related_data([], 'overrides');
$this->assertEmpty($data);
}
public function test_export_data_for_user_grades(): void {
$dg = $this->getDataGenerator();
$c1 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$cm1 = $dg->create_module('lesson', ['course' => $c1]);
$cm2 = $dg->create_module('lesson', ['course' => $c1]);
$cm1ctx = \context_module::instance($cm1->cmid);
$cm2ctx = \context_module::instance($cm2->cmid);
$now = time();
$this->create_grade($cm2, $u1, ['grade' => 33.33, 'completed' => $now - 3600]);
$this->create_grade($cm2, $u1, ['grade' => 50, 'completed' => $now - 1600]);
$this->create_grade($cm2, $u1, ['grade' => 81.23, 'completed' => $now - 100]);
$this->create_grade($cm1, $u2, ['grade' => 99.98, 'completed' => $now - 86400]);
provider::export_user_data(new approved_contextlist($u1, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id]));
$data = writer::with_context($cm1ctx)->get_related_data([], 'grades');
$this->assertEmpty($data);
$data = writer::with_context($cm2ctx)->get_related_data([], 'grades');
$this->assertNotEmpty($data);
$this->assertCount(3, $data->grades);
$this->assertEquals(33.33, $data->grades[0]->grade);
$this->assertEquals(50, $data->grades[1]->grade);
$this->assertEquals(81.23, $data->grades[2]->grade);
$this->assertEquals(transform::datetime($now - 3600), $data->grades[0]->completed);
$this->assertEquals(transform::datetime($now - 1600), $data->grades[1]->completed);
$this->assertEquals(transform::datetime($now - 100), $data->grades[2]->completed);
writer::reset();
provider::export_user_data(new approved_contextlist($u2, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id]));
$data = writer::with_context($cm2ctx)->get_related_data([], 'grades');
$this->assertEmpty($data);
$data = writer::with_context($cm1ctx)->get_related_data([], 'grades');
$this->assertNotEmpty($data);
$this->assertCount(1, $data->grades);
$this->assertEquals(99.98, $data->grades[0]->grade);
$this->assertEquals(transform::datetime($now - 86400), $data->grades[0]->completed);
}
public function test_export_data_for_user_timers(): void {
$dg = $this->getDataGenerator();
$c1 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$cm1 = $dg->create_module('lesson', ['course' => $c1]);
$cm2 = $dg->create_module('lesson', ['course' => $c1]);
$cm1ctx = \context_module::instance($cm1->cmid);
$cm2ctx = \context_module::instance($cm2->cmid);
$now = time();
$this->create_timer($cm2, $u1, ['starttime' => $now - 2000, 'lessontime' => $now + 3600, 'completed' => 0,
'timemodifiedoffline' => $now - 7000]);
$this->create_timer($cm2, $u1, ['starttime' => $now - 1000, 'lessontime' => $now + 1600, 'completed' => 0]);
$this->create_timer($cm2, $u1, ['starttime' => $now - 500, 'lessontime' => $now + 100, 'completed' => 1]);
$this->create_timer($cm1, $u2, ['starttime' => $now - 1000, 'lessontime' => $now + 1800, 'completed' => 1]);
provider::export_user_data(new approved_contextlist($u1, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id]));
$data = writer::with_context($cm1ctx)->get_related_data([], 'timers');
$this->assertEmpty($data);
$data = writer::with_context($cm2ctx)->get_related_data([], 'timers');
$this->assertNotEmpty($data);
$this->assertCount(3, $data->timers);
$this->assertEquals(transform::datetime($now - 2000), $data->timers[0]->starttime);
$this->assertEquals(transform::datetime($now + 3600), $data->timers[0]->lastactivity);
$this->assertEquals(transform::yesno(false), $data->timers[0]->completed);
$this->assertEquals(transform::datetime($now - 7000), $data->timers[0]->timemodifiedoffline);
$this->assertEquals(transform::datetime($now - 1000), $data->timers[1]->starttime);
$this->assertEquals(transform::datetime($now + 1600), $data->timers[1]->lastactivity);
$this->assertEquals(transform::yesno(false), $data->timers[1]->completed);
$this->assertNull($data->timers[1]->timemodifiedoffline);
$this->assertEquals(transform::datetime($now - 500), $data->timers[2]->starttime);
$this->assertEquals(transform::datetime($now + 100), $data->timers[2]->lastactivity);
$this->assertEquals(transform::yesno(true), $data->timers[2]->completed);
$this->assertNull($data->timers[2]->timemodifiedoffline);
writer::reset();
provider::export_user_data(new approved_contextlist($u2, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id]));
$data = writer::with_context($cm2ctx)->get_related_data([], 'timers');
$this->assertEmpty($data);
$data = writer::with_context($cm1ctx)->get_related_data([], 'timers');
$this->assertCount(1, $data->timers);
$this->assertEquals(transform::datetime($now - 1000), $data->timers[0]->starttime);
$this->assertEquals(transform::datetime($now + 1800), $data->timers[0]->lastactivity);
$this->assertEquals(transform::yesno(true), $data->timers[0]->completed);
$this->assertNull($data->timers[0]->timemodifiedoffline);
}
public function test_export_data_for_user_attempts(): void {
global $DB;
$dg = $this->getDataGenerator();
$lg = $dg->get_plugin_generator('mod_lesson');
$c1 = $dg->create_course();
$u1 = $dg->create_user();
$u2 = $dg->create_user();
$cm1 = $dg->create_module('lesson', ['course' => $c1]);
$cm2 = $dg->create_module('lesson', ['course' => $c1]);
$cm1ctx = \context_module::instance($cm1->cmid);
$cm2ctx = \context_module::instance($cm2->cmid);
$page1 = $lg->create_content($cm1);
$page2 = $lg->create_question_truefalse($cm1);
$page3 = $lg->create_question_multichoice($cm1);
$page4 = $lg->create_question_multichoice($cm1, [
'qoption' => 1,
'answer_editor' => [
['text' => 'Cats', 'format' => FORMAT_PLAIN, 'score' => 1],
['text' => 'Dogs', 'format' => FORMAT_PLAIN, 'score' => 1],
['text' => 'Birds', 'format' => FORMAT_PLAIN, 'score' => 0],
],
'jumpto' => [LESSON_NEXTPAGE, LESSON_NEXTPAGE, LESSON_THISPAGE]
]);
$page4answers = array_keys($DB->get_records('lesson_answers', ['pageid' => $page4->id], 'id'));
$page5 = $lg->create_question_matching($cm1, [
'answer_editor' => [
2 => ['text' => 'The plural of cat', 'format' => FORMAT_PLAIN],
3 => ['text' => 'The plural of dog', 'format' => FORMAT_PLAIN],
4 => ['text' => 'The plural of bird', 'format' => FORMAT_PLAIN],
],
'response_editor' => [
2 => 'Cats',
3 => 'Dogs',
4 => 'Birds',
]
]);
$page6 = $lg->create_question_shortanswer($cm1);
$page7 = $lg->create_question_numeric($cm1);
$page8 = $lg->create_question_essay($cm1);
$page9 = $lg->create_content($cm1);
$pageb1 = $lg->create_content($cm2);
$pageb2 = $lg->create_question_truefalse($cm2);
$pageb3 = $lg->create_question_truefalse($cm2);
$this->create_branch($cm1, $u1, ['pageid' => $page1->id, 'nextpageid' => $page2->id]);
$this->create_attempt($cm1, $u1, ['pageid' => $page2->id, 'useranswer' => 'This is true']);
$this->create_attempt($cm1, $u1, ['pageid' => $page3->id, 'useranswer' => 'A', 'correct' => 1]);
$this->create_attempt($cm1, $u1, ['pageid' => $page4->id,
'useranswer' => implode(',', array_slice($page4answers, 0, 2))]);
$this->create_attempt($cm1, $u1, ['pageid' => $page5->id, 'useranswer' => 'Cats,Birds,Dogs']);
$this->create_attempt($cm1, $u1, ['pageid' => $page6->id, 'useranswer' => 'Hello world!']);
$this->create_attempt($cm1, $u1, ['pageid' => $page7->id, 'useranswer' => '1337']);
$this->create_attempt($cm1, $u1, ['pageid' => $page8->id, 'useranswer' => serialize((object) [
'sent' => 0, 'graded' => 0, 'score' => 0, 'answer' => 'I like cats', 'answerformat' => FORMAT_PLAIN,
'response' => 'Me too!', 'responseformat' => FORMAT_PLAIN
])]);
$this->create_branch($cm1, $u1, ['pageid' => $page9->id, 'nextpageid' => 0]);
provider::export_user_data(new approved_contextlist($u1, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id]));
$data = writer::with_context($cm2ctx)->get_related_data([], 'attempts');
$this->assertEmpty($data);
$data = writer::with_context($cm1ctx)->get_related_data([], 'attempts');
$this->assertNotEmpty($data);
$this->assertCount(1, $data->attempts);
$this->assertEquals(1, $data->attempts[0]->number);
$this->assertCount(2, $data->attempts[0]->jumps);
$this->assertCount(7, $data->attempts[0]->answers);
$jump = $data->attempts[0]->jumps[0];
$this->assert_attempt_page($page1, $jump);
$this->assertTrue(strpos($jump['went_to'], $page2->title) !== false);
$jump = $data->attempts[0]->jumps[1];
$this->assert_attempt_page($page9, $jump);
$this->assertEquals(get_string('endoflesson', 'mod_lesson'), $jump['went_to']);
$answer = $data->attempts[0]->answers[0];
$this->assert_attempt_page($page2, $answer);
$this->assertEquals(transform::yesno(false), $answer['correct']);
$this->assertEquals('This is true', $answer['answer']);
$answer = $data->attempts[0]->answers[1];
$this->assert_attempt_page($page3, $answer);
$this->assertEquals(transform::yesno(true), $answer['correct']);
$this->assertEquals('A', $answer['answer']);
$answer = $data->attempts[0]->answers[2];
$this->assert_attempt_page($page4, $answer);
$this->assertEquals(transform::yesno(false), $answer['correct']);
$this->assertCount(2, $answer['answer']);
$this->assertTrue(in_array('Cats', $answer['answer']));
$this->assertTrue(in_array('Dogs', $answer['answer']));
$answer = $data->attempts[0]->answers[3];
$this->assert_attempt_page($page5, $answer);
$this->assertEquals(transform::yesno(false), $answer['correct']);
$this->assertCount(3, $answer['answer']);
$this->assertEquals('The plural of cat', $answer['answer'][0]['label']);
$this->assertEquals('Cats', $answer['answer'][0]['matched_with']);
$this->assertEquals('The plural of dog', $answer['answer'][1]['label']);
$this->assertEquals('Birds', $answer['answer'][1]['matched_with']);
$this->assertEquals('The plural of bird', $answer['answer'][2]['label']);
$this->assertEquals('Dogs', $answer['answer'][2]['matched_with']);
$answer = $data->attempts[0]->answers[4];
$this->assert_attempt_page($page6, $answer);
$this->assertEquals(transform::yesno(false), $answer['correct']);
$this->assertEquals('Hello world!', $answer['answer']);
$answer = $data->attempts[0]->answers[5];
$this->assert_attempt_page($page7, $answer);
$this->assertEquals(transform::yesno(false), $answer['correct']);
$this->assertEquals('1337', $answer['answer']);
$answer = $data->attempts[0]->answers[6];
$this->assert_attempt_page($page8, $answer);
$this->assertEquals(transform::yesno(false), $answer['correct']);
$this->assertEquals('I like cats', $answer['answer']);
$this->assertEquals('Me too!', $answer['response']);
writer::reset();
provider::export_user_data(new approved_contextlist($u2, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id]));
$data = writer::with_context($cm1ctx)->get_related_data([], 'attempts');
$this->assertEmpty($data);
$data = writer::with_context($cm2ctx)->get_related_data([], 'attempts');
$this->assertEmpty($data);
// Let's mess with the data by creating an additional attempt for u1, and create data for u1 and u2 in the other cm.
$this->create_branch($cm1, $u1, ['pageid' => $page1->id, 'nextpageid' => $page3->id, 'retry' => 1]);
$this->create_attempt($cm1, $u1, ['pageid' => $page3->id, 'useranswer' => 'B', 'retry' => 1]);
$this->create_branch($cm2, $u1, ['pageid' => $pageb1->id, 'nextpageid' => $pageb2->id]);
$this->create_attempt($cm2, $u1, ['pageid' => $pageb2->id, 'useranswer' => 'Abc']);
$this->create_branch($cm2, $u2, ['pageid' => $pageb1->id, 'nextpageid' => $pageb3->id]);
$this->create_attempt($cm2, $u2, ['pageid' => $pageb3->id, 'useranswer' => 'Def']);
writer::reset();
provider::export_user_data(new approved_contextlist($u1, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id]));
$data = writer::with_context($cm1ctx)->get_related_data([], 'attempts');
$this->assertNotEmpty($data);
$this->assertCount(2, $data->attempts);
$this->assertEquals(1, $data->attempts[0]->number);
$this->assertCount(2, $data->attempts[0]->jumps);
$this->assertCount(7, $data->attempts[0]->answers);
$attempt = $data->attempts[1];
$this->assertEquals(2, $attempt->number);
$this->assertCount(1, $attempt->jumps);
$this->assertCount(1, $attempt->answers);
$this->assert_attempt_page($page1, $attempt->jumps[0]);
$this->assertTrue(strpos($attempt->jumps[0]['went_to'], $page3->title) !== false);
$this->assert_attempt_page($page3, $attempt->answers[0]);
$this->assertEquals('B', $attempt->answers[0]['answer']);
$data = writer::with_context($cm2ctx)->get_related_data([], 'attempts');
$this->assertCount(1, $data->attempts);
$attempt = $data->attempts[0];
$this->assertEquals(1, $attempt->number);
$this->assertCount(1, $attempt->jumps);
$this->assertCount(1, $attempt->answers);
$this->assert_attempt_page($pageb1, $attempt->jumps[0]);
$this->assertTrue(strpos($attempt->jumps[0]['went_to'], $pageb2->title) !== false);
$this->assert_attempt_page($pageb2, $attempt->answers[0]);
$this->assertEquals('Abc', $attempt->answers[0]['answer']);
writer::reset();
provider::export_user_data(new approved_contextlist($u2, 'mod_lesson', [$cm1ctx->id, $cm2ctx->id]));
$data = writer::with_context($cm1ctx)->get_related_data([], 'attempts');
$this->assertEmpty($data);
$data = writer::with_context($cm2ctx)->get_related_data([], 'attempts');
$this->assertCount(1, $data->attempts);
$attempt = $data->attempts[0];
$this->assertEquals(1, $attempt->number);
$this->assertCount(1, $attempt->jumps);
$this->assertCount(1, $attempt->answers);
$this->assert_attempt_page($pageb1, $attempt->jumps[0]);
$this->assertTrue(strpos($attempt->jumps[0]['went_to'], $pageb3->title) !== false);
$this->assert_attempt_page($pageb3, $attempt->answers[0]);
$this->assertEquals('Def', $attempt->answers[0]['answer']);
}
/**
* Assert the page details of an attempt.
*
* @param object $page The expected page info.
* @param array $attempt The exported attempt details.
* @return void
*/
protected function assert_attempt_page($page, $attempt) {
$this->assertEquals($page->id, $attempt['id']);
$this->assertEquals($page->title, $attempt['page']);
$this->assertEquals(format_text($page->contents, $page->contentsformat), $attempt['contents']);
}
/**
* Create an attempt (answer to a question).
*
* @param object $lesson The lesson.
* @param object $user The user.
* @param array $options Options.
* @return object
*/
protected function create_attempt($lesson, $user, array $options = []) {
global $DB;
$record = (object) array_merge([
'lessonid' => $lesson->id,
'userid' => $user->id,
'pageid' => 0,
'answerid' => 0,
'retry' => 0,
'correct' => 0,
'useranswer' => '',
'timeseen' => time(),
], $options);
$record->id = $DB->insert_record('lesson_attempts', $record);
return $record;
}
/**
* Create a grade.
*
* @param object $lesson The lesson.
* @param object $user The user.
* @param array $options Options.
* @return object
*/
protected function create_grade($lesson, $user, array $options = []) {
global $DB;
$record = (object) array_merge([
'lessonid' => $lesson->id,
'userid' => $user->id,
'late' => 0,
'grade' => 50.0,
'completed' => time(),
], $options);
$record->id = $DB->insert_record('lesson_grades', $record);
return $record;
}
/**
* Create a timer.
*
* @param object $lesson The lesson.
* @param object $user The user.
* @param array $options Options.
* @return object
*/
protected function create_timer($lesson, $user, array $options = []) {
global $DB;
$record = (object) array_merge([
'lessonid' => $lesson->id,
'userid' => $user->id,
'starttime' => time() - 600,
'lessontime' => time(),
'completed' => 1,
'timemodifiedoffline' => 0,
], $options);
$record->id = $DB->insert_record('lesson_timer', $record);
return $record;
}
/**
* Create a branch (choice on page).
*
* @param object $lesson The lesson.
* @param object $user The user.
* @param array $options Options.
* @return object
*/
protected function create_branch($lesson, $user, array $options = []) {
global $DB;
$record = (object) array_merge([
'lessonid' => $lesson->id,
'userid' => $user->id,
'pageid' => 0,
'retry' => 0,
'flag' => 0,
'timeseen' => time(),
'nextpageid' => 0,
], $options);
$record->id = $DB->insert_record('lesson_branch', $record);
return $record;
}
/**
* Create an override.
*
* @param object $lesson The lesson.
* @param object $user The user.
* @param array $options Options.
* @return object
*/
protected function create_override($lesson, $user, array $options = []) {
global $DB;
$record = (object) array_merge([
'lessonid' => $lesson->id,
'userid' => $user->id,
], $options);
$record->id = $DB->insert_record('lesson_overrides', $record);
return $record;
}
}