Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   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  namespace core_courseformat;
  18  
  19  use stdClass;
  20  
  21  /**
  22   * Tests for the stateupdates class.
  23   *
  24   * @package    core_courseformat
  25   * @category   test
  26   * @copyright  2021 Sara Arjona (sara@moodle.com)
  27   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  28   * @coversDefaultClass \core_courseformat\stateupdates
  29   */
  30  class stateupdates_test extends \advanced_testcase {
  31  
  32      /**
  33       * Test for add_course_put.
  34       *
  35       * @dataProvider add_course_put_provider
  36       * @covers ::add_course_put
  37       *
  38       * @param string $role the user role in the course
  39       */
  40      public function test_add_course_put(string $role): void {
  41          global $PAGE;
  42  
  43          $this->resetAfterTest();
  44  
  45          $course = $this->getDataGenerator()->create_course(['format' => 'topics']);
  46  
  47          // Create and enrol user using given role.
  48          if ($role == 'admin') {
  49              $this->setAdminUser();
  50          } else {
  51              $user = $this->getDataGenerator()->create_user();
  52              if ($role != 'unenroled') {
  53                  $this->getDataGenerator()->enrol_user($user->id, $course->id, $role);
  54              }
  55              $this->setUser($user);
  56          }
  57  
  58          // Initialise stateupdates.
  59          $format = course_get_format($course);
  60          $updates = new stateupdates($format);
  61  
  62          // Get the expected export.
  63          $renderer = $format->get_renderer($PAGE);
  64          $stateclass = $format->get_output_classname("state\\course");
  65          $currentstate = new $stateclass($format);
  66          $expected = $currentstate->export_for_template($renderer);
  67  
  68          $updates->add_course_put();
  69  
  70          $updatelist = $updates->jsonSerialize();
  71          $this->assertCount(1, $updatelist);
  72  
  73          $update = array_pop($updatelist);
  74          $this->assertEquals('put', $update->action);
  75          $this->assertEquals('course', $update->name);
  76          $this->assertEquals($expected, $update->fields);
  77      }
  78  
  79      /**
  80       * Data provider for test_add_course_put.
  81       *
  82       * @return array testing scenarios
  83       */
  84      public function add_course_put_provider() {
  85          return [
  86              'Admin role' => [
  87                  'admin',
  88              ],
  89              'Teacher role' => [
  90                  'editingteacher',
  91              ],
  92              'Student role' => [
  93                  'student',
  94              ],
  95          ];
  96      }
  97  
  98      /**
  99       * Helper methods to find a specific update in the updadelist.
 100       *
 101       * @param array $updatelist the update list
 102       * @param string $action the action to find
 103       * @param string $name the element name to find
 104       * @param int $identifier the element id value
 105       * @return stdClass|null the object found, if any.
 106       */
 107      private function find_update(
 108          array $updatelist,
 109          string $action,
 110          string $name,
 111          int $identifier
 112      ): ?stdClass {
 113          foreach ($updatelist as $update) {
 114              if ($update->action != $action || $update->name != $name) {
 115                  continue;
 116              }
 117              if (!isset($update->fields->id)) {
 118                  continue;
 119              }
 120              if ($update->fields->id == $identifier) {
 121                  return $update;
 122              }
 123          }
 124          return null;
 125      }
 126  
 127      /**
 128       * Add track about a section state update.
 129       *
 130       * @dataProvider add_section_provider
 131       * @covers ::add_section_create
 132       * @covers ::add_section_remove
 133       * @covers ::add_section_put
 134       *
 135       * @param string $action the action name
 136       * @param string $role the user role name
 137       * @param array $expected the expected results
 138       */
 139      public function test_add_section(string $action, string $role, array $expected): void {
 140          global $PAGE, $DB;
 141  
 142          $this->resetAfterTest();
 143  
 144          $course = $this->getDataGenerator()->create_course(['numsections' => 2, 'format' => 'topics']);
 145  
 146          // Set section 2 hidden.
 147          set_section_visible($course->id, 2, 0);
 148  
 149          // Create and enrol user using given role.
 150          if ($role == 'admin') {
 151              $this->setAdminUser();
 152          } else {
 153              $user = $this->getDataGenerator()->create_user();
 154              if ($role != 'unenroled') {
 155                  $this->getDataGenerator()->enrol_user($user->id, $course->id, $role);
 156              }
 157              $this->setUser($user);
 158          }
 159  
 160          // Initialise stateupdates.
 161          $format = course_get_format($course);
 162          $updates = new stateupdates($format);
 163  
 164          $modinfo = $format->get_modinfo();
 165  
 166          // Get the expected export.
 167          $renderer = $format->get_renderer($PAGE);
 168          $stateclass = $format->get_output_classname("state\\section");
 169  
 170          // Execute method for both sections.
 171          $method = "add_section_{$action}";
 172          $sections = $modinfo->get_section_info_all();
 173          foreach ($sections as $section) {
 174              $updates->$method($section->id);
 175          }
 176  
 177          $updatelist = $updates->jsonSerialize();
 178          $this->assertCount(count($expected), $updatelist);
 179  
 180          foreach ($expected as $sectionnum) {
 181              $section = $sections[$sectionnum];
 182              $currentstate = new $stateclass($format, $section);
 183              $expected = $currentstate->export_for_template($renderer);
 184  
 185              $update = $this->find_update($updatelist, $action, 'section', $section->id);
 186              $this->assertEquals($action, $update->action);
 187              $this->assertEquals('section', $update->name);
 188              // Delete does not provide all fields.
 189              if ($action == 'remove') {
 190                  $this->assertEquals($section->id, $update->fields->id);
 191              } else {
 192                  $this->assertEquals($expected, $update->fields);
 193              }
 194          }
 195      }
 196  
 197      /**
 198       * Data provider for test_add_section.
 199       *
 200       * @return array testing scenarios
 201       */
 202      public function add_section_provider(): array {
 203          return array_merge(
 204              $this->add_section_provider_helper('put'),
 205              $this->add_section_provider_helper('create'),
 206              $this->add_section_provider_helper('remove'),
 207          );
 208      }
 209  
 210      /**
 211       * Helper for add_section_provider scenarios.
 212       *
 213       * @param string $action the action to perform
 214       * @return array testing scenarios
 215       */
 216      private function add_section_provider_helper(string $action): array {
 217          // Delete does not depends on user permissions.
 218          if ($action == 'remove') {
 219              $studentsections = [0, 1, 2];
 220          } else {
 221              $studentsections = [0, 1];
 222          }
 223  
 224          return [
 225              "$action admin role" => [
 226                  'action' => $action,
 227                  'role' => 'admin',
 228                  'expected' => [0, 1, 2],
 229              ],
 230              "$action teacher role" => [
 231                  'action' => $action,
 232                  'role' => 'editingteacher',
 233                  'expected' => [0, 1, 2],
 234              ],
 235              "$action student role" => [
 236                  'action' => $action,
 237                  'role' => 'student',
 238                  'expected' => $studentsections,
 239              ],
 240          ];
 241      }
 242  
 243  
 244      /**
 245       * Add track about a course module state update.
 246       *
 247       * @dataProvider add_cm_provider
 248       * @covers ::add_cm_put
 249       * @covers ::add_cm_create
 250       * @covers ::add_cm_remove
 251       *
 252       * @param string $action the action name
 253       * @param string $role the user role name
 254       * @param array $expected the expected results
 255       */
 256      public function test_add_cm(string $action, string $role, array $expected): void {
 257          global $PAGE, $DB;
 258  
 259          $this->resetAfterTest();
 260  
 261          $course = $this->getDataGenerator()->create_course(['numsections' => 2, 'format' => 'topics']);
 262  
 263          // Set section 2 hidden.
 264          set_section_visible($course->id, 2, 0);
 265  
 266          // Create 2 activities on each section.
 267          $activities = [];
 268          $activities[] = $this->getDataGenerator()->create_module(
 269              'book',
 270              ['course' => $course->id],
 271              ['section' => 1, 'visible' => true]
 272          );
 273          $activities[] = $this->getDataGenerator()->create_module(
 274              'book',
 275              ['course' => $course->id],
 276              ['section' => 1, 'visible' => false]
 277          );
 278          $activities[] = $this->getDataGenerator()->create_module(
 279              'book',
 280              ['course' => $course->id],
 281              ['section' => 2, 'visible' => true]
 282          );
 283          $activities[] = $this->getDataGenerator()->create_module(
 284              'book',
 285              ['course' => $course->id],
 286              ['section' => 2, 'visible' => false]
 287          );
 288  
 289          // Create and enrol user using given role.
 290          if ($role == 'admin') {
 291              $this->setAdminUser();
 292          } else {
 293              $user = $this->getDataGenerator()->create_user();
 294              if ($role != 'unenroled') {
 295                  $this->getDataGenerator()->enrol_user($user->id, $course->id, $role);
 296              }
 297              $this->setUser($user);
 298          }
 299  
 300          // Initialise stateupdates.
 301          $format = course_get_format($course);
 302          $updates = new stateupdates($format);
 303  
 304          $modinfo = $format->get_modinfo();
 305  
 306          // Get the expected export.
 307          $renderer = $format->get_renderer($PAGE);
 308          $stateclass = $format->get_output_classname("state\\cm");
 309  
 310          // Execute method for both sections.
 311          $method = "add_cm_{$action}";
 312  
 313          foreach ($activities as $activity) {
 314              $updates->$method($activity->cmid);
 315          }
 316  
 317          $updatelist = $updates->jsonSerialize();
 318          $this->assertCount(count($expected), $updatelist);
 319  
 320          foreach ($expected as $cmnum) {
 321              $activity = $activities[$cmnum];
 322  
 323              $cm = $modinfo->get_cm($activity->cmid);
 324              $section = $modinfo->get_section_info($cm->sectionnum);
 325  
 326              $currentstate = new $stateclass($format, $section, $cm);
 327              $expected = $currentstate->export_for_template($renderer);
 328  
 329              $update = $this->find_update($updatelist, $action, 'cm', $cm->id);
 330              $this->assertEquals($action, $update->action);
 331              $this->assertEquals('cm', $update->name);
 332              // Delete does not provide all fields.
 333              if ($action == 'remove') {
 334                  $this->assertEquals($cm->id, $update->fields->id);
 335              } else {
 336                  $this->assertEquals($expected, $update->fields);
 337              }
 338          }
 339      }
 340  
 341      /**
 342       * Data provider for test_add_cm.
 343       *
 344       * @return array testing scenarios
 345       */
 346      public function add_cm_provider(): array {
 347          return array_merge(
 348              $this->add_cm_provider_helper('put'),
 349              $this->add_cm_provider_helper('create'),
 350              $this->add_cm_provider_helper('remove'),
 351          );
 352      }
 353  
 354      /**
 355       * Helper for add_cm_provider scenarios.
 356       *
 357       * @param string $action the action to perform
 358       * @return array testing scenarios
 359       */
 360      private function add_cm_provider_helper(string $action): array {
 361          // Delete does not depends on user permissions.
 362          if ($action == 'remove') {
 363              $studentcms = [0, 1, 2, 3];
 364          } else {
 365              $studentcms = [0];
 366          }
 367  
 368          return [
 369              "$action admin role" => [
 370                  'action' => $action,
 371                  'role' => 'admin',
 372                  'expected' => [0, 1, 2, 3],
 373              ],
 374              "$action teacher role" => [
 375                  'action' => $action,
 376                  'role' => 'editingteacher',
 377                  'expected' => [0, 1, 2, 3],
 378              ],
 379              "$action student role" => [
 380                  'action' => $action,
 381                  'role' => 'student',
 382                  'expected' => $studentcms,
 383              ],
 384          ];
 385      }
 386  }