Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

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