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.

Differences Between: [Versions 401 and 402] [Versions 401 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  namespace core_courseformat\external;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  global $CFG;
  22  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  23  
  24  use external_api;
  25  
  26  /**
  27   * Tests for the get_state class.
  28   *
  29   * @package    core_course
  30   * @category   test
  31   * @copyright  2021 Sara Arjona (sara@moodle.com)
  32   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  33   * @coversDefaultClass \core_courseformat\external\get_state
  34   */
  35  class get_state_test extends \externallib_advanced_testcase {
  36  
  37      /** @var array Sections in the testing course. */
  38      private $sections;
  39  
  40      /** @var array Activities in the testing course. */
  41      private $activities;
  42  
  43      /**
  44       * Setup to ensure that fixtures are loaded.
  45       */
  46      public static function setupBeforeClass(): void {
  47          global $CFG;
  48  
  49          require_once($CFG->dirroot . '/course/lib.php');
  50          require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest.php');
  51          require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest_output_course_format_state.php');
  52      }
  53  
  54      /**
  55       * Setup testcase.
  56       */
  57      public function setUp(): void {
  58          $this->resetAfterTest();
  59  
  60          $this->sections = [];
  61          $this->activities = [];
  62      }
  63  
  64      /**
  65       * Test tearDown.
  66       */
  67      public function tearDown(): void {
  68          unset($this->sections);
  69          unset($this->activities);
  70      }
  71  
  72      /**
  73       * Test the behaviour of get_state::execute().
  74       *
  75       * @dataProvider get_state_provider
  76       * @covers ::execute
  77       *
  78       * @param string $role The role of the user that will execute the method.
  79       * @param string $format The course format of the course where the method will be executed.
  80       * @param string|null $expectedexception If this call will raise an exception, this is its name.
  81       */
  82      public function test_get_state(string $role, string $format = 'topics', ?string $expectedexception = null): void {
  83          $this->resetAfterTest();
  84  
  85          // Create a course.
  86          $numsections = 6;
  87          $visiblesections = $numsections + 1; // Include topic 0.
  88          $course = $this->getDataGenerator()->create_course(['numsections' => $numsections, 'format' => $format]);
  89          $hiddensections = [4, 6];
  90          foreach ($hiddensections as $section) {
  91              set_section_visible($course->id, $section, 0);
  92          }
  93  
  94          // Create and enrol user.
  95          $isadmin = ($role == 'admin');
  96          $canedit = $isadmin || ($role == 'editingteacher');
  97          if ($isadmin) {
  98              $this->setAdminUser();
  99          } else {
 100              if (!$canedit) {
 101                  // User won't see the hidden sections. Remove them from the total.
 102                  $visiblesections = $visiblesections - count($hiddensections);
 103              }
 104              $user = $this->getDataGenerator()->create_user();
 105              if ($role != 'unenroled') {
 106                  $this->getDataGenerator()->enrol_user($user->id, $course->id, $role);
 107              }
 108              $this->setUser($user);
 109          }
 110  
 111          // Add some activities to the course.
 112          $this->create_activity($course->id, 'page', 1, true, $canedit);
 113          $this->create_activity($course->id, 'forum', 1, true, $canedit);
 114          $this->create_activity($course->id, 'book', 1, false, $canedit);
 115          $this->create_activity($course->id, 'assign', 2, false, $canedit);
 116          $this->create_activity($course->id, 'glossary', 4, true, $canedit);
 117          $this->create_activity($course->id, 'label', 5, false, $canedit);
 118          $this->create_activity($course->id, 'feedback', 5, true, $canedit);
 119  
 120          if ($expectedexception) {
 121              $this->expectException($expectedexception);
 122          }
 123  
 124          // Get course state.
 125          $result = get_state::execute($course->id);
 126          $result = external_api::clean_returnvalue(get_state::execute_returns(), $result);
 127          $result = json_decode($result);
 128          if ($format == 'theunittest') {
 129              // These course format's hasn't the renderer file, so a debugging message will be displayed.
 130              $this->assertDebuggingCalled();
 131          }
 132  
 133          // Check course information.
 134          $this->assertEquals($numsections, $result->course->numsections);
 135          $this->assertCount($visiblesections, $result->section);
 136          $this->assertCount(count($this->activities), $result->cm);
 137          $this->assertCount(count($result->course->sectionlist), $result->section);
 138          if ($format == 'theunittest') {
 139              $this->assertTrue(property_exists($result->course, 'newfancyelement'));
 140          } else {
 141              $this->assertFalse(property_exists($result->course, 'newfancyelement'));
 142          }
 143  
 144          // Check sections information.
 145          foreach ($result->section as $section) {
 146              if (in_array($section->number, $hiddensections)) {
 147                  $this->assertFalse($section->visible);
 148              } else {
 149                  $this->assertTrue($section->visible);
 150              }
 151              // Check section is defined in course->sectionlist.
 152              $this->assertContains($section->id, $result->course->sectionlist);
 153              // Check course modules list for this section is the expected.
 154              if (array_key_exists($section->number, $this->sections)) {
 155                  $this->assertEquals($this->sections[$section->number], $section->cmlist);
 156              }
 157          }
 158          // Check course modules information.
 159          foreach ($result->cm as $cm) {
 160              $this->assertEquals($this->activities[$cm->id]->name, $cm->name);
 161              $this->assertEquals((bool) $this->activities[$cm->id]->visible, $cm->visible);
 162          }
 163      }
 164  
 165      /**
 166       * Data provider for test_get_state().
 167       *
 168       * @return array
 169       */
 170      public function get_state_provider(): array {
 171          return [
 172              // ROLES. Testing behaviour depending on the user role calling the method.
 173              'Admin user should work' => [
 174                  'role' => 'admin',
 175              ],
 176              'Editing teacher should work' => [
 177                  'role' => 'editingteacher',
 178              ],
 179              'Student should work' => [
 180                  'role' => 'student',
 181              ],
 182              'Unenroled user should raise an exception' => [
 183                  'role' => 'unenroled',
 184                  'format' => 'topics',
 185                  'expectedexception' => 'moodle_exception',
 186              ],
 187  
 188              // COURSEFORMAT. Test behaviour depending on course formats.
 189              'Single activity format should work (admin)' => [
 190                  'role' => 'admin',
 191                  'format' => 'singleactivity',
 192              ],
 193              'Social format should work (admin)' => [
 194                  'role' => 'admin',
 195                  'format' => 'social',
 196              ],
 197              'Weeks format should work (admin)' => [
 198                  'role' => 'admin',
 199                  'format' => 'weeks',
 200              ],
 201              'The unit tests format should work (admin)' => [
 202                  'role' => 'admin',
 203                  'format' => 'theunittest',
 204              ],
 205              'Single activity format should work (student)' => [
 206                  'role' => 'student',
 207                  'format' => 'singleactivity',
 208              ],
 209              'Social format should work (student)' => [
 210                  'role' => 'student',
 211                  'format' => 'social',
 212              ],
 213              'Weeks format should work (student)' => [
 214                  'role' => 'student',
 215                  'format' => 'weeks',
 216              ],
 217              'The unit tests format should work (student)' => [
 218                  'role' => 'student',
 219                  'format' => 'theunittest',
 220              ],
 221              'Single activity format should raise an exception (unenroled)' => [
 222                  'role' => 'unenroled',
 223                  'format' => 'singleactivity',
 224                  'expectedexception' => 'moodle_exception',
 225              ],
 226              'Social format should raise an exception (unenroled)' => [
 227                  'role' => 'unenroled',
 228                  'format' => 'social',
 229                  'expectedexception' => 'moodle_exception',
 230              ],
 231              'Weeks format should raise an exception (unenroled)' => [
 232                  'role' => 'unenroled',
 233                  'format' => 'weeks',
 234                  'expectedexception' => 'moodle_exception',
 235              ],
 236              'The unit tests format should raise an exception (unenroled)' => [
 237                  'role' => 'unenroled',
 238                  'format' => 'theunittest',
 239                  'expectedexception' => 'moodle_exception',
 240              ],
 241          ];
 242      }
 243  
 244      /**
 245       * Helper method to create an activity into a section and add it to the $sections and $activities arrays.
 246       * For non-admin users, only visible activities will be added to the activities and sections arrays.
 247       *
 248       * @param int $courseid Course identifier where the activity will be added.
 249       * @param string $type Activity type ('forum', 'assign', ...).
 250       * @param int $section Section number where the activity will be added.
 251       * @param bool $visible Whether the activity will be visible or not.
 252       * @param bool $canedit Whether the activity will be accessed later by a user with editing capabilities
 253       */
 254      private function create_activity(int $courseid, string $type, int $section, bool $visible = true, bool $canedit = true): void {
 255          $activity = $this->getDataGenerator()->create_module(
 256              $type,
 257              ['course' => $courseid],
 258              ['section' => $section, 'visible' => $visible]
 259          );
 260  
 261          list(, $activitycm) = get_course_and_cm_from_instance($activity->id, $type);
 262  
 263          if ($visible || $canedit) {
 264              $this->activities[$activitycm->id] = $activitycm;
 265              $this->sections[$section][] = $activitycm->id;
 266          }
 267      }
 268  }