Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Events tests.
  19   *
  20   * @package core_question
  21   * @copyright 2014 Mark Nelson <markn@moodle.com>
  22   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_question\event;
  26  
  27  use qtype_description;
  28  use qtype_description_edit_form;
  29  use qtype_description_test_helper;
  30  use question_category_object;
  31  use question_edit_contexts;
  32  use test_question_maker;
  33  
  34  defined('MOODLE_INTERNAL') || die();
  35  
  36  global $CFG;
  37  
  38  require_once($CFG->dirroot . '/question/editlib.php');
  39  require_once($CFG->dirroot . '/question/category_class.php');
  40  
  41  class events_test extends \advanced_testcase {
  42  
  43      /**
  44       * Tests set up.
  45       */
  46      public function setUp(): void {
  47          $this->resetAfterTest();
  48      }
  49  
  50      /**
  51       * Test the question category created event.
  52       */
  53      public function test_question_category_created() {
  54          $this->setAdminUser();
  55          $course = $this->getDataGenerator()->create_course();
  56          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
  57  
  58          $contexts = new question_edit_contexts(\context_module::instance($quiz->cmid));
  59  
  60          $defaultcategoryobj = question_make_default_categories(array($contexts->lowest()));
  61          $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
  62  
  63          $qcobject = new question_category_object(
  64              1,
  65              new \moodle_url('/mod/quiz/edit.php', array('cmid' => $quiz->cmid)),
  66              $contexts->having_one_edit_tab_cap('categories'),
  67              $defaultcategoryobj->id,
  68              $defaultcategory,
  69              null,
  70              $contexts->having_cap('moodle/question:add'));
  71  
  72          // Trigger and capture the event.
  73          $sink = $this->redirectEvents();
  74          $categoryid = $qcobject->add_category($defaultcategory, 'newcategory', '', true);
  75          $events = $sink->get_events();
  76          $event = reset($events);
  77  
  78          // Check that the event data is valid.
  79          $this->assertInstanceOf('\core\event\question_category_created', $event);
  80          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
  81          $expected = array($course->id, 'quiz', 'addcategory', 'view.php?id=' . $quiz->cmid , $categoryid, $quiz->cmid);
  82          $this->assertEventLegacyLogData($expected, $event);
  83          $this->assertEventContextNotUsed($event);
  84      }
  85  
  86      /**
  87       * Test the question category deleted event.
  88       */
  89      public function test_question_category_deleted() {
  90          $this->setAdminUser();
  91          $course = $this->getDataGenerator()->create_course();
  92          $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
  93  
  94          $contexts = new question_edit_contexts(\context_module::instance($quiz->cmid));
  95  
  96          $defaultcategoryobj = question_make_default_categories([$contexts->lowest()]);
  97          $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
  98  
  99          $qcobject = new question_category_object(
 100                  1,
 101                  new \moodle_url('/mod/quiz/edit.php', ['cmid' => $quiz->cmid]),
 102                  $contexts->having_one_edit_tab_cap('categories'),
 103                  $defaultcategoryobj->id,
 104                  $defaultcategory,
 105                  null,
 106                  $contexts->having_cap('moodle/question:add'));
 107  
 108          // Create the category.
 109          $categoryid = $qcobject->add_category($defaultcategory, 'newcategory', '', true);
 110  
 111          // Trigger and capture the event.
 112          $sink = $this->redirectEvents();
 113          $qcobject->delete_category($categoryid);
 114          $events = $sink->get_events();
 115          $event = reset($events);
 116  
 117          // Check that the event data is valid.
 118          $this->assertInstanceOf('\core\event\question_category_deleted', $event);
 119          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 120          $this->assertEquals($categoryid, $event->objectid);
 121          $this->assertDebuggingNotCalled();
 122      }
 123  
 124      /**
 125       * Test the question category updated event.
 126       */
 127      public function test_question_category_updated() {
 128          $this->setAdminUser();
 129          $course = $this->getDataGenerator()->create_course();
 130          $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
 131  
 132          $contexts = new question_edit_contexts(\context_module::instance($quiz->cmid));
 133  
 134          $defaultcategoryobj = question_make_default_categories([$contexts->lowest()]);
 135          $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
 136  
 137          $qcobject = new question_category_object(
 138                  1,
 139                  new \moodle_url('/mod/quiz/edit.php', ['cmid' => $quiz->cmid]),
 140                  $contexts->having_one_edit_tab_cap('categories'),
 141                  $defaultcategoryobj->id,
 142                  $defaultcategory,
 143                  null,
 144                  $contexts->having_cap('moodle/question:add'));
 145  
 146          // Create the category.
 147          $categoryid = $qcobject->add_category($defaultcategory, 'newcategory', '', true);
 148  
 149          // Trigger and capture the event.
 150          $sink = $this->redirectEvents();
 151          $qcobject->update_category($categoryid, $defaultcategory, 'updatedcategory', '', FORMAT_HTML, '', false);
 152          $events = $sink->get_events();
 153          $event = reset($events);
 154  
 155          // Check that the event data is valid.
 156          $this->assertInstanceOf('\core\event\question_category_updated', $event);
 157          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 158          $this->assertEquals($categoryid, $event->objectid);
 159          $this->assertDebuggingNotCalled();
 160      }
 161  
 162      /**
 163       * Test the question category viewed event.
 164       * There is no external API for viewing the category, so the unit test will simply
 165       * create and trigger the event and ensure data is returned as expected.
 166       */
 167      public function test_question_category_viewed() {
 168  
 169          $this->setAdminUser();
 170          $course = $this->getDataGenerator()->create_course();
 171          $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
 172  
 173          $contexts = new question_edit_contexts(\context_module::instance($quiz->cmid));
 174  
 175          $defaultcategoryobj = question_make_default_categories([$contexts->lowest()]);
 176          $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
 177  
 178          $qcobject = new question_category_object(
 179                  1,
 180                  new \moodle_url('/mod/quiz/edit.php', ['cmid' => $quiz->cmid]),
 181                  $contexts->having_one_edit_tab_cap('categories'),
 182                  $defaultcategoryobj->id,
 183                  $defaultcategory,
 184                  null,
 185                  $contexts->having_cap('moodle/question:add'));
 186  
 187          // Create the category.
 188          $categoryid = $qcobject->add_category($defaultcategory, 'newcategory', '', true);
 189  
 190          // Log the view of this category.
 191          $category = new \stdClass();
 192          $category->id = $categoryid;
 193          $context = \context_module::instance($quiz->cmid);
 194          $event = \core\event\question_category_viewed::create_from_question_category_instance($category, $context);
 195  
 196          // Trigger and capture the event.
 197          $sink = $this->redirectEvents();
 198          $event->trigger();
 199          $events = $sink->get_events();
 200          $event = reset($events);
 201  
 202          // Check that the event data is valid.
 203          $this->assertInstanceOf('\core\event\question_category_viewed', $event);
 204          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 205          $this->assertEquals($categoryid, $event->objectid);
 206          $this->assertDebuggingNotCalled();
 207  
 208      }
 209  
 210      /**
 211       * Test the questions imported event.
 212       * There is no easy way to trigger this event using the API, so the unit test will simply
 213       * create and trigger the event and ensure data is returned as expected.
 214       */
 215      public function test_questions_imported() {
 216  
 217          $this->setAdminUser();
 218          $course = $this->getDataGenerator()->create_course();
 219          $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
 220  
 221          $contexts = new question_edit_contexts(\context_module::instance($quiz->cmid));
 222  
 223          $defaultcategoryobj = question_make_default_categories([$contexts->lowest()]);
 224          $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
 225  
 226          $qcobject = new question_category_object(
 227                  1,
 228                  new \moodle_url('/mod/quiz/edit.php', ['cmid' => $quiz->cmid]),
 229                  $contexts->having_one_edit_tab_cap('categories'),
 230                  $defaultcategoryobj->id,
 231                  $defaultcategory,
 232                  null,
 233                  $contexts->having_cap('moodle/question:add'));
 234  
 235          // Create the category.
 236          $categoryid = $qcobject->add_category($defaultcategory, 'newcategory', '', true);
 237  
 238          // Log the view of this category.
 239          $params = [
 240                  'context' => \context_module::instance($quiz->cmid),
 241                  'other' => ['categoryid' => $categoryid, 'format' => 'testformat'],
 242          ];
 243  
 244          $event = \core\event\questions_imported::create($params);
 245  
 246          // Trigger and capture the event.
 247          $sink = $this->redirectEvents();
 248          $event->trigger();
 249          $events = $sink->get_events();
 250          $event = reset($events);
 251  
 252          // Check that the event data is valid.
 253          $this->assertInstanceOf('\core\event\questions_imported', $event);
 254          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 255          $this->assertEquals($categoryid, $event->other['categoryid']);
 256          $this->assertEquals('testformat', $event->other['format']);
 257          $this->assertDebuggingNotCalled();
 258  
 259      }
 260  
 261      /**
 262       * Test the questions exported event.
 263       * There is no easy way to trigger this event using the API, so the unit test will simply
 264       * create and trigger the event and ensure data is returned as expected.
 265       */
 266      public function test_questions_exported() {
 267  
 268          $this->setAdminUser();
 269          $course = $this->getDataGenerator()->create_course();
 270          $quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
 271  
 272          $contexts = new question_edit_contexts(\context_module::instance($quiz->cmid));
 273  
 274          $defaultcategoryobj = question_make_default_categories([$contexts->lowest()]);
 275          $defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
 276  
 277          $qcobject = new question_category_object(
 278                  1,
 279                  new \moodle_url('/mod/quiz/edit.php', ['cmid' => $quiz->cmid]),
 280                  $contexts->having_one_edit_tab_cap('categories'),
 281                  $defaultcategoryobj->id,
 282                  $defaultcategory,
 283                  null,
 284                  $contexts->having_cap('moodle/question:add'));
 285  
 286          // Create the category.
 287          $categoryid = $qcobject->add_category($defaultcategory, 'newcategory', '', true);
 288  
 289          // Log the view of this category.
 290          $params = [
 291                  'context' => \context_module::instance($quiz->cmid),
 292                  'other' => ['categoryid' => $categoryid, 'format' => 'testformat'],
 293          ];
 294  
 295          $event = \core\event\questions_exported::create($params);
 296  
 297          // Trigger and capture the event.
 298          $sink = $this->redirectEvents();
 299          $event->trigger();
 300          $events = $sink->get_events();
 301          $event = reset($events);
 302  
 303          // Check that the event data is valid.
 304          $this->assertInstanceOf('\core\event\questions_exported', $event);
 305          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 306          $this->assertEquals($categoryid, $event->other['categoryid']);
 307          $this->assertEquals('testformat', $event->other['format']);
 308          $this->assertDebuggingNotCalled();
 309  
 310      }
 311  
 312      /**
 313       * Test the question created event.
 314       */
 315      public function test_question_created() {
 316  
 317          $this->setAdminUser();
 318          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 319  
 320          $cat = $generator->create_question_category(['name' => 'My category', 'sortorder' => 1]);
 321  
 322          // Trigger and capture the event.
 323          $sink = $this->redirectEvents();
 324          $questiondata = $generator->create_question('description', null, ['category' => $cat->id]);
 325          $question = \question_bank::load_question($questiondata->id);
 326  
 327          $events = $sink->get_events();
 328          $event = reset($events);
 329  
 330          // Check that the event data is valid.
 331          $this->assertInstanceOf('\core\event\question_created', $event);
 332          $this->assertEquals($question->id, $event->objectid);
 333          $this->assertEquals($cat->id, $event->other['categoryid']);
 334          $this->assertDebuggingNotCalled();
 335  
 336      }
 337  
 338      /**
 339       * Test the question deleted event.
 340       */
 341      public function test_question_deleted() {
 342  
 343          $this->setAdminUser();
 344          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 345  
 346          $cat = $generator->create_question_category(['name' => 'My category', 'sortorder' => 1]);
 347  
 348          $questiondata = $generator->create_question('description', null, ['category' => $cat->id]);
 349          $question = \question_bank::load_question($questiondata->id);
 350  
 351          // Trigger and capture the event.
 352          $sink = $this->redirectEvents();
 353          question_delete_question($question->id);
 354          $events = $sink->get_events();
 355          $event = reset($events);
 356  
 357          // Check that the event data is valid.
 358          $this->assertInstanceOf('\core\event\question_deleted', $event);
 359          $this->assertEquals($question->id, $event->objectid);
 360          $this->assertEquals($cat->id, $event->other['categoryid']);
 361          $this->assertDebuggingNotCalled();
 362  
 363      }
 364  
 365      /**
 366       * Test the question updated event.
 367       */
 368      public function test_question_updated() {
 369  
 370          global $CFG;
 371          require_once($CFG->dirroot . '/question/type/description/questiontype.php');
 372          require_once($CFG->dirroot . '/question/type/edit_question_form.php');
 373          require_once($CFG->dirroot . '/question/type/description/edit_description_form.php');
 374  
 375          $this->setAdminUser();
 376          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 377  
 378          $cat = $generator->create_question_category(['name' => 'My category', 'sortorder' => 1]);
 379  
 380          $questiondata = $generator->create_question('description', null, ['category' => $cat->id]);
 381          $question = \question_bank::load_question($questiondata->id);
 382  
 383          $qtype = new qtype_description();
 384          $formdata = test_question_maker::get_question_form_data('description');
 385          $formdata->category = "{$cat->id},{$cat->contextid}";
 386          qtype_description_edit_form::mock_submit((array) $formdata);
 387  
 388          $form = qtype_description_test_helper::get_question_editing_form($cat, $questiondata);
 389          $fromform = $form->get_data();
 390  
 391          // Trigger and capture the event.
 392          $sink = $this->redirectEvents();
 393          $qtype->save_question($questiondata, $fromform);
 394          $events = $sink->get_events();
 395          $event = reset($events);
 396  
 397          // Check that the event data is valid.
 398          $this->assertInstanceOf('\core\event\question_updated', $event);
 399          $this->assertEquals($question->id, $event->objectid);
 400          $this->assertEquals($cat->id, $event->other['categoryid']);
 401          $this->assertDebuggingNotCalled();
 402  
 403      }
 404  
 405      /**
 406       * Test the question moved event.
 407       */
 408      public function test_question_moved() {
 409  
 410          $this->setAdminUser();
 411          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 412  
 413          $cat1 = $generator->create_question_category([
 414                  'name' => 'My category 1', 'sortorder' => 1]);
 415  
 416          $cat2 = $generator->create_question_category([
 417                  'name' => 'My category 2', 'sortorder' => 2]);
 418  
 419          $questiondata = $generator->create_question('description', null, ['category' => $cat1->id]);
 420          $question = \question_bank::load_question($questiondata->id);
 421  
 422          // Trigger and capture the event.
 423          $sink = $this->redirectEvents();
 424          question_move_questions_to_category([$question->id], $cat2->id);
 425          $events = $sink->get_events();
 426          $event = reset($events);
 427  
 428          // Check that the event data is valid.
 429          $this->assertInstanceOf('\core\event\question_moved', $event);
 430          $this->assertEquals($question->id, $event->objectid);
 431          $this->assertEquals($cat1->id, $event->other['oldcategoryid']);
 432          $this->assertEquals($cat2->id, $event->other['newcategoryid']);
 433          $this->assertDebuggingNotCalled();
 434  
 435      }
 436  
 437      /**
 438       * Test the question viewed event.
 439       * There is no external API for viewing the question, so the unit test will simply
 440       * create and trigger the event and ensure data is returned as expected.
 441       */
 442      public function test_question_viewed() {
 443  
 444          $this->setAdminUser();
 445          $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
 446  
 447          $cat = $generator->create_question_category(['name' => 'My category', 'sortorder' => 1]);
 448  
 449          $questiondata = $generator->create_question('description', null, ['category' => $cat->id]);
 450          $question = \question_bank::load_question($questiondata->id);
 451  
 452          $event = \core\event\question_viewed::create_from_question_instance($question, \context::instance_by_id($cat->contextid));
 453  
 454          // Trigger and capture the event.
 455          $sink = $this->redirectEvents();
 456          $event->trigger();
 457          $events = $sink->get_events();
 458          $event = reset($events);
 459  
 460          // Check that the event data is valid.
 461          $this->assertInstanceOf('\core\event\question_viewed', $event);
 462          $this->assertEquals($question->id, $event->objectid);
 463          $this->assertEquals($cat->id, $event->other['categoryid']);
 464          $this->assertDebuggingNotCalled();
 465  
 466      }
 467  }