MDL-77581 behat: Create generators for lesson pages and answers

This commit is contained in:
Dani Palou 2023-06-02 12:36:39 +02:00
parent 1184d3c20f
commit 40b918348c
3 changed files with 776 additions and 7 deletions

View File

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

View File

@ -25,6 +25,8 @@
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
/**
* mod_lesson data generator class.
*
@ -40,6 +42,24 @@ class mod_lesson_generator extends testing_module_generator {
*/
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,
];
/**
* To be called from data reset code only,
* do not use in tests.
@ -47,9 +67,17 @@ class mod_lesson_generator extends testing_module_generator {
*/
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;
@ -92,9 +120,84 @@ class mod_lesson_generator extends testing_module_generator {
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;
}
$funcname = $qtype === 'content' ? 'create_content' : "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 int Page ID.
*/
public function create_content($lesson, $record = array()) {
global $DB, $CFG;
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$now = time();
$this->pagecount++;
$record = (array)$record + array(
@ -124,7 +227,6 @@ class mod_lesson_generator extends testing_module_generator {
*/
public function create_question_truefalse($lesson, $record = array()) {
global $DB, $CFG;
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$now = time();
$this->pagecount++;
$record = (array)$record + array(
@ -177,7 +279,6 @@ class mod_lesson_generator extends testing_module_generator {
*/
public function create_question_multichoice($lesson, $record = array()) {
global $DB, $CFG;
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$now = time();
$this->pagecount++;
$record = (array)$record + array(
@ -230,7 +331,6 @@ class mod_lesson_generator extends testing_module_generator {
*/
public function create_question_essay($lesson, $record = array()) {
global $DB, $CFG;
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$now = time();
$this->pagecount++;
$record = (array)$record + array(
@ -272,7 +372,6 @@ class mod_lesson_generator extends testing_module_generator {
*/
public function create_question_matching($lesson, $record = array()) {
global $DB, $CFG;
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$now = time();
$this->pagecount++;
$record = (array)$record + array(
@ -351,7 +450,6 @@ class mod_lesson_generator extends testing_module_generator {
*/
public function create_question_shortanswer($lesson, $record = array()) {
global $DB, $CFG;
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$now = time();
$this->pagecount++;
$record = (array)$record + array(
@ -393,7 +491,6 @@ class mod_lesson_generator extends testing_module_generator {
*/
public function create_question_numeric($lesson, $record = array()) {
global $DB, $CFG;
require_once($CFG->dirroot.'/mod/lesson/locallib.php');
$now = time();
$this->pagecount++;
$record = (array)$record + array(
@ -431,6 +528,7 @@ class mod_lesson_generator extends testing_module_generator {
* 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;
@ -449,4 +547,159 @@ class mod_lesson_generator extends testing_module_generator {
$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;
}
}

View File

@ -23,6 +23,7 @@ namespace 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 {
@ -213,4 +214,454 @@ class generator_test extends \advanced_testcase {
$this->assertEquals($page2->id, $records[$page2->id]->id);
$this->assertEquals($page2->title, $records[$page2->id]->title);
}
/**
* 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], '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], '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], '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], '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();
}
}