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 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   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   * Tests for the moodle_page class.
  19   *
  20   * @package   core
  21   * @category  test
  22   * @copyright 2009 Tim Hunt
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace core;
  27  
  28  use moodle_page;
  29  
  30  defined('MOODLE_INTERNAL') || die();
  31  
  32  global $CFG;
  33  require_once($CFG->libdir . '/pagelib.php');
  34  require_once($CFG->libdir . '/blocklib.php');
  35  
  36  /**
  37   * Tests for the moodle_page class.
  38   *
  39   * @package   core
  40   * @category  test
  41   * @copyright 2009 Tim Hunt
  42   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  43   * @covers    \moodle_page
  44   */
  45  class moodle_page_test extends \advanced_testcase {
  46  
  47      /**
  48       * @var testable_moodle_page
  49       */
  50      protected $testpage;
  51  
  52      public function setUp(): void {
  53          parent::setUp();
  54          $this->resetAfterTest();
  55          $this->testpage = new testable_moodle_page();
  56      }
  57  
  58      public function test_course_returns_site_before_set() {
  59          global $SITE;
  60          // Validated.
  61          $this->assertSame($SITE, $this->testpage->course);
  62      }
  63  
  64      public function test_setting_course_works() {
  65          // Setup fixture.
  66          $course = $this->getDataGenerator()->create_course();
  67          $this->testpage->set_context(\context_system::instance()); // Avoid trying to set the context.
  68          // Exercise SUT.
  69          $this->testpage->set_course($course);
  70          // Validated.
  71          $this->assertEquals($course, $this->testpage->course);
  72      }
  73  
  74      public function test_global_course_and_page_course_are_same_with_global_page() {
  75          global $COURSE, $PAGE;
  76          // Setup fixture.
  77          $course = $this->getDataGenerator()->create_course();
  78          $this->testpage->set_context(\context_system::instance()); // Avoid trying to set the context.
  79          $PAGE = $this->testpage;
  80          // Exercise SUT.
  81          $this->testpage->set_course($course);
  82          // Validated.
  83          $this->assertSame($COURSE, $this->testpage->course);
  84      }
  85  
  86      public function test_global_course_not_changed_with_non_global_page() {
  87          global $COURSE;
  88          $originalcourse = $COURSE;
  89          // Setup fixture.
  90          $course = $this->getDataGenerator()->create_course();
  91          $this->testpage->set_context(\context_system::instance()); // Avoid trying to set the context.
  92          // Exercise SUT.
  93          $this->testpage->set_course($course);
  94          // Validated.
  95          $this->assertSame($originalcourse, $COURSE);
  96      }
  97  
  98      public function test_cannot_set_course_once_theme_set() {
  99          // Setup fixture.
 100          $this->testpage->force_theme(\theme_config::DEFAULT_THEME);
 101          $course = $this->getDataGenerator()->create_course();
 102  
 103          // Exercise SUT.
 104          $this->expectException(\coding_exception::class);
 105          $this->testpage->set_course($course);
 106      }
 107  
 108      public function test_cannot_set_category_once_theme_set() {
 109          // Setup fixture.
 110          $this->testpage->force_theme(\theme_config::DEFAULT_THEME);
 111  
 112          // Exercise SUT.
 113          $this->expectException(\coding_exception::class);
 114          $this->testpage->set_category_by_id(123);
 115      }
 116  
 117      public function test_cannot_set_category_once_course_set() {
 118          // Setup fixture.
 119          $course = $this->getDataGenerator()->create_course();
 120          $this->testpage->set_context(\context_system::instance()); // Avoid trying to set the context.
 121          $this->testpage->set_course($course);
 122  
 123          // Exercise SUT.
 124          $this->expectException(\coding_exception::class);
 125          $this->testpage->set_category_by_id(123);
 126      }
 127  
 128      public function test_categories_array_empty_for_front_page() {
 129          global $SITE;
 130          // Setup fixture.
 131          $this->testpage->set_context(\context_system::instance()); // Avoid trying to set the context.
 132          $this->testpage->set_course($SITE);
 133          // Exercise SUT and validate.
 134          $this->assertEquals(array(), $this->testpage->categories);
 135      }
 136  
 137      public function test_set_state_normal_path() {
 138          $course = $this->getDataGenerator()->create_course();
 139          $this->testpage->set_context(\context_system::instance());
 140          $this->testpage->set_course($course);
 141  
 142          $this->assertEquals(\moodle_page::STATE_BEFORE_HEADER, $this->testpage->state);
 143  
 144          $this->testpage->set_state(\moodle_page::STATE_PRINTING_HEADER);
 145          $this->assertEquals(\moodle_page::STATE_PRINTING_HEADER, $this->testpage->state);
 146  
 147          $this->testpage->set_state(\moodle_page::STATE_IN_BODY);
 148          $this->assertEquals(\moodle_page::STATE_IN_BODY, $this->testpage->state);
 149  
 150          $this->testpage->set_state(\moodle_page::STATE_DONE);
 151          $this->assertEquals(\moodle_page::STATE_DONE, $this->testpage->state);
 152      }
 153  
 154      public function test_set_state_cannot_skip_one() {
 155          // Exercise SUT.
 156          $this->expectException(\coding_exception::class);
 157          $this->testpage->set_state(\moodle_page::STATE_IN_BODY);
 158      }
 159  
 160      public function test_header_printed_false_initially() {
 161          // Validated.
 162          $this->assertFalse($this->testpage->headerprinted);
 163      }
 164  
 165      public function test_header_printed_becomes_true() {
 166          $course = $this->getDataGenerator()->create_course();
 167          $this->testpage->set_context(\context_system::instance());
 168          $this->testpage->set_course($course);
 169  
 170          // Exercise SUT.
 171          $this->testpage->set_state(\moodle_page::STATE_PRINTING_HEADER);
 172          $this->testpage->set_state(\moodle_page::STATE_IN_BODY);
 173          // Validated.
 174          $this->assertTrue($this->testpage->headerprinted);
 175      }
 176  
 177      public function test_set_context() {
 178          // Setup fixture.
 179          $course = $this->getDataGenerator()->create_course();
 180          $context = \context_course::instance($course->id);
 181          // Exercise SUT.
 182          $this->testpage->set_context($context);
 183          // Validated.
 184          $this->assertSame($context, $this->testpage->context);
 185      }
 186  
 187      public function test_pagetype_defaults_to_script() {
 188          global $SCRIPT;
 189          // Exercise SUT and validate.
 190          $SCRIPT = '/index.php';
 191          $this->testpage->initialise_default_pagetype();
 192          $this->assertSame('site-index', $this->testpage->pagetype);
 193      }
 194  
 195      public function test_set_pagetype() {
 196          // Exercise SUT.
 197          $this->testpage->set_pagetype('a-page-type');
 198          // Validated.
 199          $this->assertSame('a-page-type', $this->testpage->pagetype);
 200      }
 201  
 202      public function test_initialise_default_pagetype() {
 203          // Exercise SUT.
 204          $this->testpage->initialise_default_pagetype('admin/tool/unittest/index.php');
 205          // Validated.
 206          $this->assertSame('admin-tool-unittest-index', $this->testpage->pagetype);
 207      }
 208  
 209      public function test_initialise_default_pagetype_fp() {
 210          // Exercise SUT.
 211          $this->testpage->initialise_default_pagetype('index.php');
 212          // Validated.
 213          $this->assertSame('site-index', $this->testpage->pagetype);
 214      }
 215  
 216      public function test_get_body_classes_empty() {
 217          // Validated.
 218          $this->assertSame('', $this->testpage->bodyclasses);
 219      }
 220  
 221      public function test_get_body_classes_single() {
 222          // Exercise SUT.
 223          $this->testpage->add_body_class('aclassname');
 224          // Validated.
 225          $this->assertSame('aclassname', $this->testpage->bodyclasses);
 226      }
 227  
 228      public function test_get_body_classes() {
 229          // Exercise SUT.
 230          $this->testpage->add_body_classes(array('aclassname', 'anotherclassname'));
 231          // Validated.
 232          $this->assertSame('aclassname anotherclassname', $this->testpage->bodyclasses);
 233      }
 234  
 235      public function test_url_to_class_name() {
 236          $this->assertSame('example-com', $this->testpage->url_to_class_name('http://example.com'));
 237          $this->assertSame('example-com--80', $this->testpage->url_to_class_name('http://example.com:80'));
 238          $this->assertSame('example-com--moodle', $this->testpage->url_to_class_name('https://example.com/moodle'));
 239          $this->assertSame('example-com--8080--nested-moodle', $this->testpage->url_to_class_name('https://example.com:8080/nested/moodle'));
 240      }
 241  
 242      public function test_set_docs_path() {
 243          // Exercise SUT.
 244          $this->testpage->set_docs_path('a/file/path');
 245          // Validated.
 246          $this->assertSame('a/file/path', $this->testpage->docspath);
 247      }
 248  
 249      public function test_docs_path_defaults_from_pagetype() {
 250          // Exercise SUT.
 251          $this->testpage->set_pagetype('a-page-type');
 252          // Validated.
 253          $this->assertSame('a/page/type', $this->testpage->docspath);
 254      }
 255  
 256      public function test_set_url_root() {
 257          global $CFG;
 258          // Exercise SUT.
 259          $this->testpage->set_url('/');
 260          // Validated.
 261          $this->assertSame($CFG->wwwroot . '/', $this->testpage->url->out());
 262      }
 263  
 264      public function test_set_url_one_param() {
 265          global $CFG;
 266          // Exercise SUT.
 267          $this->testpage->set_url('/mod/quiz/attempt.php', array('attempt' => 123));
 268          // Validated.
 269          $this->assertSame($CFG->wwwroot . '/mod/quiz/attempt.php?attempt=123', $this->testpage->url->out());
 270      }
 271  
 272      public function test_set_url_two_params() {
 273          global $CFG;
 274          // Exercise SUT.
 275          $this->testpage->set_url('/mod/quiz/attempt.php', array('attempt' => 123, 'page' => 7));
 276          // Validated.
 277          $this->assertSame($CFG->wwwroot . '/mod/quiz/attempt.php?attempt=123&amp;page=7', $this->testpage->url->out());
 278      }
 279  
 280      public function test_set_url_using_moodle_url() {
 281          global $CFG;
 282          // Fixture setup.
 283          $url = new \moodle_url('/mod/workshop/allocation.php', array('cmid' => 29, 'method' => 'manual'));
 284          // Exercise SUT.
 285          $this->testpage->set_url($url);
 286          // Validated.
 287          $this->assertSame($CFG->wwwroot . '/mod/workshop/allocation.php?cmid=29&amp;method=manual', $this->testpage->url->out());
 288      }
 289  
 290      public function test_set_url_sets_page_type() {
 291          // Exercise SUT.
 292          $this->testpage->set_url('/mod/quiz/attempt.php', array('attempt' => 123, 'page' => 7));
 293          // Validated.
 294          $this->assertSame('mod-quiz-attempt', $this->testpage->pagetype);
 295      }
 296  
 297      public function test_set_url_does_not_change_explicit_page_type() {
 298          // Setup fixture.
 299          $this->testpage->set_pagetype('a-page-type');
 300          // Exercise SUT.
 301          $this->testpage->set_url('/mod/quiz/attempt.php', array('attempt' => 123, 'page' => 7));
 302          // Validated.
 303          $this->assertSame('a-page-type', $this->testpage->pagetype);
 304      }
 305  
 306      public function test_set_subpage() {
 307          // Exercise SUT.
 308          $this->testpage->set_subpage('somestring');
 309          // Validated.
 310          $this->assertSame('somestring', $this->testpage->subpage);
 311      }
 312  
 313      public function test_set_heading() {
 314          // Exercise SUT.
 315          $this->testpage->set_heading('a heading');
 316          // Validated.
 317          $this->assertSame('a heading', $this->testpage->heading);
 318  
 319          // By default formatting is applied and tags are removed.
 320          $this->testpage->set_heading('a heading <a href="#">edit</a><p>');
 321          $this->assertSame('a heading edit', $this->testpage->heading);
 322  
 323          // Without formatting the tags are preserved but cleaned.
 324          $this->testpage->set_heading('a heading <a href="#">edit</a><p>', false);
 325          $this->assertSame('a heading <a href="#">edit</a><p></p>', $this->testpage->heading);
 326      }
 327  
 328      public function test_set_title() {
 329          // Exercise SUT.
 330          $this->testpage->set_title('a title');
 331          // Validated.
 332          $this->assertSame('a title', $this->testpage->title);
 333      }
 334  
 335      public function test_default_pagelayout() {
 336          // Exercise SUT and Validate.
 337          $this->assertSame('base', $this->testpage->pagelayout);
 338      }
 339  
 340      public function test_set_pagelayout() {
 341          // Exercise SUT.
 342          $this->testpage->set_pagelayout('type');
 343          // Validated.
 344          $this->assertSame('type', $this->testpage->pagelayout);
 345      }
 346  
 347      public function test_setting_course_sets_context() {
 348          // Setup fixture.
 349          $course = $this->getDataGenerator()->create_course();
 350          $context = \context_course::instance($course->id);
 351  
 352          // Exercise SUT.
 353          $this->testpage->set_course($course);
 354  
 355          // Validated.
 356          $this->assertSame($context, $this->testpage->context);
 357      }
 358  
 359      public function test_set_category_top_level() {
 360          global $DB;
 361          // Setup fixture.
 362          $cat = $this->getDataGenerator()->create_category();
 363          $catdbrecord = $DB->get_record('course_categories', array('id' => $cat->id));
 364          // Exercise SUT.
 365          $this->testpage->set_category_by_id($cat->id);
 366          // Validated.
 367          $this->assertEquals($catdbrecord, $this->testpage->category);
 368          $this->assertSame(\context_coursecat::instance($cat->id), $this->testpage->context);
 369      }
 370  
 371      public function test_set_nested_categories() {
 372          global $DB;
 373          // Setup fixture.
 374          $topcat = $this->getDataGenerator()->create_category();
 375          $topcatdbrecord = $DB->get_record('course_categories', array('id' => $topcat->id));
 376          $subcat = $this->getDataGenerator()->create_category(array('parent'=>$topcat->id));
 377          $subcatdbrecord = $DB->get_record('course_categories', array('id' => $subcat->id));
 378          // Exercise SUT.
 379          $this->testpage->set_category_by_id($subcat->id);
 380          // Validated.
 381          $categories = $this->testpage->categories;
 382          $this->assertCount(2, $categories);
 383          $this->assertEquals($topcatdbrecord, array_pop($categories));
 384          $this->assertEquals($subcatdbrecord, array_pop($categories));
 385      }
 386  
 387      public function test_cm_null_initially() {
 388          // Validated.
 389          $this->assertNull($this->testpage->cm);
 390      }
 391  
 392      public function test_set_cm() {
 393          // Setup fixture.
 394          $course = $this->getDataGenerator()->create_course();
 395          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 396          $cm = get_coursemodule_from_id('forum', $forum->cmid);
 397          // Exercise SUT.
 398          $this->testpage->set_cm($cm);
 399          // Validated.
 400          $this->assertEquals($cm->id, $this->testpage->cm->id);
 401      }
 402  
 403      public function test_cannot_set_activity_record_before_cm() {
 404          // Setup fixture.
 405          $course = $this->getDataGenerator()->create_course();
 406          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 407          $cm = get_coursemodule_from_id('forum', $forum->cmid);
 408          // Exercise SUT.
 409          $this->expectException(\coding_exception::class);
 410          $this->testpage->set_activity_record($forum);
 411      }
 412  
 413      public function test_setting_cm_sets_context() {
 414          // Setup fixture.
 415          $course = $this->getDataGenerator()->create_course();
 416          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 417          $cm = get_coursemodule_from_id('forum', $forum->cmid);
 418          // Exercise SUT.
 419          $this->testpage->set_cm($cm);
 420          // Validated.
 421          $this->assertSame(\context_module::instance($cm->id), $this->testpage->context);
 422      }
 423  
 424      public function test_activity_record_loaded_if_not_set() {
 425          // Setup fixture.
 426          $course = $this->getDataGenerator()->create_course();
 427          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 428          $cm = get_coursemodule_from_id('forum', $forum->cmid);
 429          // Exercise SUT.
 430          $this->testpage->set_cm($cm);
 431          // Validated.
 432          unset($forum->cmid);
 433          $this->assertEquals($forum, $this->testpage->activityrecord);
 434      }
 435  
 436      public function test_set_activity_record() {
 437          // Setup fixture.
 438          $course = $this->getDataGenerator()->create_course();
 439          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 440          $cm = get_coursemodule_from_id('forum', $forum->cmid);
 441          $this->testpage->set_cm($cm);
 442          // Exercise SUT.
 443          $this->testpage->set_activity_record($forum);
 444          // Validated.
 445          unset($forum->cmid);
 446          $this->assertEquals($forum, $this->testpage->activityrecord);
 447      }
 448  
 449      public function test_cannot_set_inconsistent_activity_record_course() {
 450          // Setup fixture.
 451          $course = $this->getDataGenerator()->create_course();
 452          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 453          $cm = get_coursemodule_from_id('forum', $forum->cmid);
 454          $this->testpage->set_cm($cm);
 455          // Exercise SUT.
 456          $forum->course = 13;
 457          $this->expectException(\coding_exception::class);
 458          $this->testpage->set_activity_record($forum);
 459      }
 460  
 461      public function test_cannot_set_inconsistent_activity_record_instance() {
 462          // Setup fixture.
 463          $course = $this->getDataGenerator()->create_course();
 464          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 465          $cm = get_coursemodule_from_id('forum', $forum->cmid);
 466          $this->testpage->set_cm($cm);
 467          // Exercise SUT.
 468          $forum->id = 13;
 469          $this->expectException(\coding_exception::class);
 470          $this->testpage->set_activity_record($forum);
 471      }
 472  
 473      public function test_setting_cm_sets_course() {
 474          // Setup fixture.
 475          $course = $this->getDataGenerator()->create_course();
 476          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 477          $cm = get_coursemodule_from_id('forum', $forum->cmid);
 478          // Exercise SUT.
 479          $this->testpage->set_cm($cm);
 480          // Validated.
 481          $this->assertEquals($course->id, $this->testpage->course->id);
 482      }
 483  
 484      public function test_set_cm_with_course_and_activity_no_db() {
 485          // Setup fixture.
 486          $course = $this->getDataGenerator()->create_course();
 487          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 488          $cm = get_coursemodule_from_id('forum', $forum->cmid);
 489          // This only works without db if we already have modinfo cache
 490          // Exercise SUT.
 491          $this->testpage->set_cm($cm, $course, $forum);
 492          // Validated.
 493          $this->assertEquals($cm->id, $this->testpage->cm->id);
 494          $this->assertEquals($course->id, $this->testpage->course->id);
 495          unset($forum->cmid);
 496          $this->assertEquals($forum, $this->testpage->activityrecord);
 497      }
 498  
 499      public function test_cannot_set_cm_with_inconsistent_course() {
 500          // Setup fixture.
 501          $course = $this->getDataGenerator()->create_course();
 502          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 503          $cm = get_coursemodule_from_id('forum', $forum->cmid);
 504          // Exercise SUT.
 505          $cm->course = 13;
 506          $this->expectException(\coding_exception::class);
 507          $this->testpage->set_cm($cm, $course);
 508      }
 509  
 510      public function test_get_activity_name() {
 511          // Setup fixture.
 512          $course = $this->getDataGenerator()->create_course();
 513          $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id));
 514          $cm = get_coursemodule_from_id('forum', $forum->cmid);
 515          // Exercise SUT.
 516          $this->testpage->set_cm($cm, $course, $forum);
 517          // Validated.
 518          $this->assertSame('forum', $this->testpage->activityname);
 519      }
 520  
 521      public function test_user_is_editing_on() {
 522          // We are relying on the fact that unit tests are always run by admin, to
 523          // ensure the user_allows_editing call returns true.
 524  
 525          // Setup fixture.
 526          global $USER;
 527  
 528          $this->testpage->set_context(\context_system::instance());
 529          $this->setAdminUser();
 530  
 531          $USER->editing = true;
 532          // Validated.
 533          $this->assertTrue($this->testpage->user_is_editing());
 534      }
 535  
 536      public function test_user_is_editing_off() {
 537          // We are relying on the fact that unit tests are always run by admin, to
 538          // ensure the user_allows_editing call returns true.
 539  
 540          // Setup fixture.
 541          global $USER;
 542  
 543          $this->testpage->set_context(\context_system::instance());
 544          $this->setAdminUser();
 545  
 546          $USER->editing = false;
 547          // Validated.
 548          $this->assertFalse($this->testpage->user_is_editing());
 549      }
 550  
 551      public function test_default_editing_capabilities() {
 552          $this->testpage->set_context(\context_system::instance());
 553          $this->setAdminUser();
 554  
 555          // Validated.
 556          $this->assertEquals(array('moodle/site:manageblocks'), $this->testpage->all_editing_caps());
 557      }
 558  
 559      public function test_other_block_editing_cap() {
 560          $this->testpage->set_context(\context_system::instance());
 561          $this->setAdminUser();
 562  
 563          // Exercise SUT.
 564          $this->testpage->set_blocks_editing_capability('moodle/my:manageblocks');
 565          // Validated.
 566          $this->assertEquals(array('moodle/my:manageblocks'), $this->testpage->all_editing_caps());
 567      }
 568  
 569      public function test_other_editing_cap() {
 570          $this->testpage->set_context(\context_system::instance());
 571          $this->setAdminUser();
 572  
 573          // Exercise SUT.
 574          $this->testpage->set_other_editing_capability('moodle/course:manageactivities');
 575          // Validated.
 576          $actualcaps = $this->testpage->all_editing_caps();
 577          $expectedcaps = array('moodle/course:manageactivities', 'moodle/site:manageblocks');
 578          $this->assertEquals(array_values($expectedcaps), array_values($actualcaps));
 579      }
 580  
 581      public function test_other_editing_caps() {
 582          $this->testpage->set_context(\context_system::instance());
 583          $this->setAdminUser();
 584  
 585          // Exercise SUT.
 586          $this->testpage->set_other_editing_capability(array('moodle/course:manageactivities', 'moodle/site:other'));
 587          // Validated.
 588          $actualcaps = $this->testpage->all_editing_caps();
 589          $expectedcaps = array('moodle/course:manageactivities', 'moodle/site:other', 'moodle/site:manageblocks');
 590          $this->assertEquals(array_values($expectedcaps), array_values($actualcaps));
 591      }
 592  
 593      /**
 594       * Test getting a renderer.
 595       */
 596      public function test_get_renderer() {
 597          global $OUTPUT, $PAGE;
 598          $oldoutput = $OUTPUT;
 599          $oldpage = $PAGE;
 600          $PAGE = $this->testpage;
 601  
 602          $this->testpage->set_pagelayout('standard');
 603          $this->assertEquals('standard', $this->testpage->pagelayout);
 604          // Initialise theme and output for the next tests.
 605          $this->testpage->initialise_theme_and_output();
 606          // Check the generated $OUTPUT object is a core renderer.
 607          $this->assertInstanceOf('core_renderer', $OUTPUT);
 608          // Check we can get a core renderer if we explicitly request one (no component).
 609          $this->assertInstanceOf('core_renderer', $this->testpage->get_renderer('core'));
 610          // Check we get a CLI renderer if we request a maintenance renderer. The CLI target should take precedence.
 611          $this->assertInstanceOf('core_renderer_cli',
 612              $this->testpage->get_renderer('core', null, RENDERER_TARGET_MAINTENANCE));
 613  
 614          // Check we can get a coures renderer if we explicitly request one (valid component).
 615          $this->assertInstanceOf('core_course_renderer', $this->testpage->get_renderer('core', 'course'));
 616  
 617          // Check a properly invalid component.
 618          try {
 619              $this->testpage->get_renderer('core', 'monkeys');
 620              $this->fail('Request for renderer with invalid component didn\'t throw expected exception.');
 621          } catch (\coding_exception $exception) {
 622              $this->assertEquals('monkeys', $exception->debuginfo);
 623          }
 624  
 625          $PAGE = $oldpage;
 626          $OUTPUT = $oldoutput;
 627      }
 628  
 629      /**
 630       * Tests getting a renderer with a maintenance layout.
 631       *
 632       * This layout has special hacks in place in order to deliver a "maintenance" renderer.
 633       */
 634      public function test_get_renderer_maintenance() {
 635          global $OUTPUT, $PAGE;
 636          $oldoutput = $OUTPUT;
 637          $oldpage = $PAGE;
 638          $PAGE = $this->testpage;
 639  
 640          $this->testpage->set_pagelayout('maintenance');
 641          $this->assertEquals('maintenance', $this->testpage->pagelayout);
 642          // Initialise theme and output for the next tests.
 643          $this->testpage->initialise_theme_and_output();
 644          // Check the generated $OUTPUT object is a core cli renderer.
 645          // It shouldn't be maintenance because there the cli target should take greater precedence.
 646          $this->assertInstanceOf('core_renderer_cli', $OUTPUT);
 647          // Check we can get a core renderer if we explicitly request one (no component).
 648          $this->assertInstanceOf('core_renderer', $this->testpage->get_renderer('core'));
 649          // Check we get a CLI renderer if we request a maintenance renderer. The CLI target should take precedence.
 650          $this->assertInstanceOf('core_renderer_cli',
 651              $this->testpage->get_renderer('core', null, RENDERER_TARGET_MAINTENANCE));
 652          // Check we can get a coures renderer if we explicitly request one (valid component).
 653          $this->assertInstanceOf('core_course_renderer', $this->testpage->get_renderer('core', 'course'));
 654  
 655          try {
 656              $this->testpage->get_renderer('core', 'monkeys');
 657              $this->fail('Request for renderer with invalid component didn\'t throw expected exception.');
 658          } catch (\coding_exception $exception) {
 659              $this->assertEquals('monkeys', $exception->debuginfo);
 660          }
 661  
 662          $PAGE = $oldpage;
 663          $OUTPUT = $oldoutput;
 664      }
 665  
 666      public function test_render_to_cli() {
 667          global $OUTPUT;
 668  
 669          $footer = $OUTPUT->footer();
 670          $this->assertEmpty($footer, 'cli output does not have a footer.');
 671      }
 672  
 673      /**
 674       * Validate the theme value depending on the user theme and cohorts.
 675       *
 676       * @dataProvider get_user_theme_provider
 677       */
 678      public function test_cohort_get_user_theme($usertheme, $sitetheme, $cohortthemes, $expected) {
 679          global $DB, $PAGE, $USER;
 680  
 681          $this->resetAfterTest();
 682  
 683          // Enable cohort themes.
 684          set_config('allowuserthemes', 1);
 685          set_config('allowcohortthemes', 1);
 686  
 687          $systemctx = \context_system::instance();
 688  
 689          set_config('theme', $sitetheme);
 690          // Create user.
 691          $user = $this->getDataGenerator()->create_user(array('theme' => $usertheme));
 692  
 693          // Create cohorts and add user as member.
 694          $cohorts = array();
 695          foreach ($cohortthemes as $cohorttheme) {
 696              $cohort = $this->getDataGenerator()->create_cohort(array('contextid' => $systemctx->id, 'name' => 'Cohort',
 697                  'idnumber' => '', 'description' => '', 'theme' => $cohorttheme));
 698              $cohorts[] = $cohort;
 699              cohort_add_member($cohort->id, $user->id);
 700          }
 701  
 702          // Get the theme and compare to the expected.
 703          $this->setUser($user);
 704  
 705          // Initialise user theme.
 706          $USER = get_complete_user_data('id', $user->id);
 707  
 708          // Initialise site theme.
 709          $PAGE->reset_theme_and_output();
 710          $PAGE->initialise_theme_and_output();
 711          $result = $PAGE->theme->name;
 712          $this->assertEquals($expected, $result);
 713      }
 714  
 715      /**
 716       * Some user cases for validating the expected theme depending on the cohorts, site and user values.
 717       *
 718       * The result is an array of:
 719       *     'User case description' => [
 720       *      'usertheme' => '', // User theme.
 721       *      'sitetheme' => '', // Site theme.
 722       *      'cohorts' => [],   // Cohort themes.
 723       *      'expected' => '',  // Expected value returned by cohort_get_user_cohort_theme.
 724       *    ]
 725       *
 726       * @return array
 727       */
 728      public function get_user_theme_provider() {
 729          return [
 730              'User not a member of any cohort' => [
 731                  'usertheme' => '',
 732                  'sitetheme' => 'boost',
 733                  'cohorts' => [],
 734                  'expected' => 'boost',
 735              ],
 736              'User member of one cohort which has a theme set' => [
 737                  'usertheme' => '',
 738                  'sitetheme' => 'boost',
 739                  'cohorts' => [
 740                      'classic',
 741                  ],
 742                  'expected' => 'classic',
 743              ],
 744              'User member of one cohort which has a theme set, and one without a theme' => [
 745                  'usertheme' => '',
 746                  'sitetheme' => 'boost',
 747                  'cohorts' => [
 748                      'classic',
 749                      '',
 750                  ],
 751                  'expected' => 'classic',
 752              ],
 753              'User member of one cohort which has a theme set, and one with a different theme' => [
 754                  'usertheme' => '',
 755                  'sitetheme' => 'boost',
 756                  'cohorts' => [
 757                      'classic',
 758                      'someother',
 759                  ],
 760                  'expected' => 'boost',
 761              ],
 762              'User with a theme but not a member of any cohort' => [
 763                  'usertheme' => 'classic',
 764                  'sitetheme' => 'boost',
 765                  'cohorts' => [],
 766                  'expected' => 'classic',
 767              ],
 768              'User with a theme and member of one cohort which has a theme set' => [
 769                  'usertheme' => 'classic',
 770                  'sitetheme' => 'boost',
 771                  'cohorts' => [
 772                      'boost',
 773                  ],
 774                  'expected' => 'classic',
 775              ],
 776          ];
 777      }
 778  }
 779  
 780  /**
 781   * Test-specific subclass to make some protected things public.
 782   */
 783  class testable_moodle_page extends moodle_page {
 784      public function initialise_default_pagetype($script = null) {
 785          parent::initialise_default_pagetype($script);
 786      }
 787      public function url_to_class_name($url) {
 788          return parent::url_to_class_name($url);
 789      }
 790      public function all_editing_caps() {
 791          return parent::all_editing_caps();
 792      }
 793  }