2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Tests for the moodle_page class.
22 * @copyright 2009 Tim Hunt
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') ||
die();
29 require_once($CFG->libdir
. '/pagelib.php');
30 require_once($CFG->libdir
. '/blocklib.php');
33 class core_moodle_page_testcase
extends advanced_testcase
{
36 * @var testable_moodle_page
40 public function setUp() {
42 $this->resetAfterTest();
43 $this->testpage
= new testable_moodle_page();
46 public function test_course_returns_site_before_set() {
49 $this->assertSame($SITE, $this->testpage
->course
);
52 public function test_setting_course_works() {
54 $course = $this->getDataGenerator()->create_course();
55 $this->testpage
->set_context(context_system
::instance()); // Avoid trying to set the context.
57 $this->testpage
->set_course($course);
59 $this->assertEquals($course, $this->testpage
->course
);
62 public function test_global_course_and_page_course_are_same_with_global_page() {
63 global $COURSE, $PAGE;
65 $course = $this->getDataGenerator()->create_course();
66 $this->testpage
->set_context(context_system
::instance()); // Avoid trying to set the context.
67 $PAGE = $this->testpage
;
69 $this->testpage
->set_course($course);
71 $this->assertSame($COURSE, $this->testpage
->course
);
74 public function test_global_course_not_changed_with_non_global_page() {
76 $originalcourse = $COURSE;
78 $course = $this->getDataGenerator()->create_course();
79 $this->testpage
->set_context(context_system
::instance()); // Avoid trying to set the context.
81 $this->testpage
->set_course($course);
83 $this->assertSame($originalcourse, $COURSE);
86 public function test_cannot_set_course_once_theme_set() {
88 $this->testpage
->force_theme(theme_config
::DEFAULT_THEME
);
89 $course = $this->getDataGenerator()->create_course();
91 $this->setExpectedException('coding_exception');
93 $this->testpage
->set_course($course);
96 public function test_cannot_set_category_once_theme_set() {
98 $this->testpage
->force_theme(theme_config
::DEFAULT_THEME
);
100 $this->setExpectedException('coding_exception');
102 $this->testpage
->set_category_by_id(123);
105 public function test_cannot_set_category_once_course_set() {
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);
111 $this->setExpectedException('coding_exception');
113 $this->testpage
->set_category_by_id(123);
116 public function test_categories_array_empty_for_front_page() {
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
);
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);
130 $this->assertEquals(moodle_page
::STATE_BEFORE_HEADER
, $this->testpage
->state
);
132 $this->testpage
->set_state(moodle_page
::STATE_PRINTING_HEADER
);
133 $this->assertEquals(moodle_page
::STATE_PRINTING_HEADER
, $this->testpage
->state
);
135 $this->testpage
->set_state(moodle_page
::STATE_IN_BODY
);
136 $this->assertEquals(moodle_page
::STATE_IN_BODY
, $this->testpage
->state
);
138 $this->testpage
->set_state(moodle_page
::STATE_DONE
);
139 $this->assertEquals(moodle_page
::STATE_DONE
, $this->testpage
->state
);
142 public function test_set_state_cannot_skip_one() {
144 $this->setExpectedException('coding_exception');
146 $this->testpage
->set_state(moodle_page
::STATE_IN_BODY
);
149 public function test_header_printed_false_initially() {
151 $this->assertFalse($this->testpage
->headerprinted
);
154 public function test_header_printed_becomes_true() {
155 $course = $this->getDataGenerator()->create_course();
156 $this->testpage
->set_context(context_system
::instance());
157 $this->testpage
->set_course($course);
160 $this->testpage
->set_state(moodle_page
::STATE_PRINTING_HEADER
);
161 $this->testpage
->set_state(moodle_page
::STATE_IN_BODY
);
163 $this->assertTrue($this->testpage
->headerprinted
);
166 public function test_set_context() {
168 $course = $this->getDataGenerator()->create_course();
169 $context = context_course
::instance($course->id
);
171 $this->testpage
->set_context($context);
173 $this->assertSame($context, $this->testpage
->context
);
176 public function test_pagetype_defaults_to_script() {
178 // Exercise SUT and validate.
179 $SCRIPT = '/index.php';
180 $this->testpage
->initialise_default_pagetype();
181 $this->assertSame('site-index', $this->testpage
->pagetype
);
184 public function test_set_pagetype() {
186 $this->testpage
->set_pagetype('a-page-type');
188 $this->assertSame('a-page-type', $this->testpage
->pagetype
);
191 public function test_initialise_default_pagetype() {
193 $this->testpage
->initialise_default_pagetype('admin/tool/unittest/index.php');
195 $this->assertSame('admin-tool-unittest-index', $this->testpage
->pagetype
);
198 public function test_initialise_default_pagetype_fp() {
200 $this->testpage
->initialise_default_pagetype('index.php');
202 $this->assertSame('site-index', $this->testpage
->pagetype
);
205 public function test_get_body_classes_empty() {
207 $this->assertSame('', $this->testpage
->bodyclasses
);
210 public function test_get_body_classes_single() {
212 $this->testpage
->add_body_class('aclassname');
214 $this->assertSame('aclassname', $this->testpage
->bodyclasses
);
217 public function test_get_body_classes() {
219 $this->testpage
->add_body_classes(array('aclassname', 'anotherclassname'));
221 $this->assertSame('aclassname anotherclassname', $this->testpage
->bodyclasses
);
224 public function test_url_to_class_name() {
225 $this->assertSame('example-com', $this->testpage
->url_to_class_name('http://example.com'));
226 $this->assertSame('example-com--80', $this->testpage
->url_to_class_name('http://example.com:80'));
227 $this->assertSame('example-com--moodle', $this->testpage
->url_to_class_name('https://example.com/moodle'));
228 $this->assertSame('example-com--8080--nested-moodle', $this->testpage
->url_to_class_name('https://example.com:8080/nested/moodle'));
231 public function test_set_docs_path() {
233 $this->testpage
->set_docs_path('a/file/path');
235 $this->assertSame('a/file/path', $this->testpage
->docspath
);
238 public function test_docs_path_defaults_from_pagetype() {
240 $this->testpage
->set_pagetype('a-page-type');
242 $this->assertSame('a/page/type', $this->testpage
->docspath
);
245 public function test_set_url_root() {
248 $this->testpage
->set_url('/');
250 $this->assertSame($CFG->wwwroot
. '/', $this->testpage
->url
->out());
253 public function test_set_url_one_param() {
256 $this->testpage
->set_url('/mod/quiz/attempt.php', array('attempt' => 123));
258 $this->assertSame($CFG->wwwroot
. '/mod/quiz/attempt.php?attempt=123', $this->testpage
->url
->out());
261 public function test_set_url_two_params() {
264 $this->testpage
->set_url('/mod/quiz/attempt.php', array('attempt' => 123, 'page' => 7));
266 $this->assertSame($CFG->wwwroot
. '/mod/quiz/attempt.php?attempt=123&page=7', $this->testpage
->url
->out());
269 public function test_set_url_using_moodle_url() {
272 $url = new moodle_url('/mod/workshop/allocation.php', array('cmid' => 29, 'method' => 'manual'));
274 $this->testpage
->set_url($url);
276 $this->assertSame($CFG->wwwroot
. '/mod/workshop/allocation.php?cmid=29&method=manual', $this->testpage
->url
->out());
279 public function test_set_url_sets_page_type() {
281 $this->testpage
->set_url('/mod/quiz/attempt.php', array('attempt' => 123, 'page' => 7));
283 $this->assertSame('mod-quiz-attempt', $this->testpage
->pagetype
);
286 public function test_set_url_does_not_change_explicit_page_type() {
288 $this->testpage
->set_pagetype('a-page-type');
290 $this->testpage
->set_url('/mod/quiz/attempt.php', array('attempt' => 123, 'page' => 7));
292 $this->assertSame('a-page-type', $this->testpage
->pagetype
);
295 public function test_set_subpage() {
297 $this->testpage
->set_subpage('somestring');
299 $this->assertSame('somestring', $this->testpage
->subpage
);
302 public function test_set_heading() {
304 $this->testpage
->set_heading('a heading');
306 $this->assertSame('a heading', $this->testpage
->heading
);
309 public function test_set_title() {
311 $this->testpage
->set_title('a title');
313 $this->assertSame('a title', $this->testpage
->title
);
316 public function test_default_pagelayout() {
317 // Exercise SUT and Validate.
318 $this->assertSame('base', $this->testpage
->pagelayout
);
321 public function test_set_pagelayout() {
323 $this->testpage
->set_pagelayout('type');
325 $this->assertSame('type', $this->testpage
->pagelayout
);
328 public function test_setting_course_sets_context() {
330 $course = $this->getDataGenerator()->create_course();
331 $context = context_course
::instance($course->id
);
334 $this->testpage
->set_course($course);
337 $this->assertSame($context, $this->testpage
->context
);
340 public function test_set_category_top_level() {
343 $cat = $this->getDataGenerator()->create_category();
344 $catdbrecord = $DB->get_record('course_categories', array('id' => $cat->id
));
346 $this->testpage
->set_category_by_id($cat->id
);
348 $this->assertEquals($catdbrecord, $this->testpage
->category
);
349 $this->assertSame(context_coursecat
::instance($cat->id
), $this->testpage
->context
);
352 public function test_set_nested_categories() {
355 $topcat = $this->getDataGenerator()->create_category();
356 $topcatdbrecord = $DB->get_record('course_categories', array('id' => $topcat->id
));
357 $subcat = $this->getDataGenerator()->create_category(array('parent'=>$topcat->id
));
358 $subcatdbrecord = $DB->get_record('course_categories', array('id' => $subcat->id
));
360 $this->testpage
->set_category_by_id($subcat->id
);
362 $categories = $this->testpage
->categories
;
363 $this->assertCount(2, $categories);
364 $this->assertEquals($topcatdbrecord, array_pop($categories));
365 $this->assertEquals($subcatdbrecord, array_pop($categories));
368 public function test_cm_null_initially() {
370 $this->assertNull($this->testpage
->cm
);
373 public function test_set_cm() {
375 $course = $this->getDataGenerator()->create_course();
376 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id
));
377 $cm = get_coursemodule_from_id('forum', $forum->cmid
);
379 $this->testpage
->set_cm($cm);
381 $this->assertEquals($cm->id
, $this->testpage
->cm
->id
);
384 public function test_cannot_set_activity_record_before_cm() {
386 $course = $this->getDataGenerator()->create_course();
387 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id
));
388 $cm = get_coursemodule_from_id('forum', $forum->cmid
);
390 $this->setExpectedException('coding_exception');
392 $this->testpage
->set_activity_record($forum);
395 public function test_setting_cm_sets_context() {
397 $course = $this->getDataGenerator()->create_course();
398 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id
));
399 $cm = get_coursemodule_from_id('forum', $forum->cmid
);
401 $this->testpage
->set_cm($cm);
403 $this->assertSame(context_module
::instance($cm->id
), $this->testpage
->context
);
406 public function test_activity_record_loaded_if_not_set() {
408 $course = $this->getDataGenerator()->create_course();
409 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id
));
410 $cm = get_coursemodule_from_id('forum', $forum->cmid
);
412 $this->testpage
->set_cm($cm);
415 $this->assertEquals($forum, $this->testpage
->activityrecord
);
418 public function test_set_activity_record() {
420 $course = $this->getDataGenerator()->create_course();
421 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id
));
422 $cm = get_coursemodule_from_id('forum', $forum->cmid
);
423 $this->testpage
->set_cm($cm);
425 $this->testpage
->set_activity_record($forum);
428 $this->assertEquals($forum, $this->testpage
->activityrecord
);
431 public function test_cannot_set_inconsistent_activity_record_course() {
433 $course = $this->getDataGenerator()->create_course();
434 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id
));
435 $cm = get_coursemodule_from_id('forum', $forum->cmid
);
436 $this->testpage
->set_cm($cm);
438 $this->setExpectedException('coding_exception');
441 $this->testpage
->set_activity_record($forum);
444 public function test_cannot_set_inconsistent_activity_record_instance() {
446 $course = $this->getDataGenerator()->create_course();
447 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id
));
448 $cm = get_coursemodule_from_id('forum', $forum->cmid
);
449 $this->testpage
->set_cm($cm);
451 $this->setExpectedException('coding_exception');
454 $this->testpage
->set_activity_record($forum);
457 public function test_setting_cm_sets_course() {
459 $course = $this->getDataGenerator()->create_course();
460 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id
));
461 $cm = get_coursemodule_from_id('forum', $forum->cmid
);
463 $this->testpage
->set_cm($cm);
465 $this->assertEquals($course->id
, $this->testpage
->course
->id
);
468 public function test_set_cm_with_course_and_activity_no_db() {
470 $course = $this->getDataGenerator()->create_course();
471 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id
));
472 $cm = get_coursemodule_from_id('forum', $forum->cmid
);
473 // This only works without db if we already have modinfo cache
475 $this->testpage
->set_cm($cm, $course, $forum);
477 $this->assertEquals($cm->id
, $this->testpage
->cm
->id
);
478 $this->assertEquals($course->id
, $this->testpage
->course
->id
);
480 $this->assertEquals($forum, $this->testpage
->activityrecord
);
483 public function test_cannot_set_cm_with_inconsistent_course() {
485 $course = $this->getDataGenerator()->create_course();
486 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id
));
487 $cm = get_coursemodule_from_id('forum', $forum->cmid
);
489 $this->setExpectedException('coding_exception');
492 $this->testpage
->set_cm($cm, $course);
495 public function test_get_activity_name() {
497 $course = $this->getDataGenerator()->create_course();
498 $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id
));
499 $cm = get_coursemodule_from_id('forum', $forum->cmid
);
501 $this->testpage
->set_cm($cm, $course, $forum);
503 $this->assertSame('forum', $this->testpage
->activityname
);
506 public function test_user_is_editing_on() {
507 // We are relying on the fact that unit tests are always run by admin, to
508 // ensure the user_allows_editing call returns true.
513 $this->testpage
->set_context(context_system
::instance());
514 $this->setAdminUser();
516 $USER->editing
= true;
518 $this->assertTrue($this->testpage
->user_is_editing());
521 public function test_user_is_editing_off() {
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.
528 $this->testpage
->set_context(context_system
::instance());
529 $this->setAdminUser();
531 $USER->editing
= false;
533 $this->assertFalse($this->testpage
->user_is_editing());
536 public function test_default_editing_capabilities() {
537 $this->testpage
->set_context(context_system
::instance());
538 $this->setAdminUser();
541 $this->assertEquals(array('moodle/site:manageblocks'), $this->testpage
->all_editing_caps());
544 public function test_other_block_editing_cap() {
545 $this->testpage
->set_context(context_system
::instance());
546 $this->setAdminUser();
549 $this->testpage
->set_blocks_editing_capability('moodle/my:manageblocks');
551 $this->assertEquals(array('moodle/my:manageblocks'), $this->testpage
->all_editing_caps());
554 public function test_other_editing_cap() {
555 $this->testpage
->set_context(context_system
::instance());
556 $this->setAdminUser();
559 $this->testpage
->set_other_editing_capability('moodle/course:manageactivities');
561 $actualcaps = $this->testpage
->all_editing_caps();
562 $expectedcaps = array('moodle/course:manageactivities', 'moodle/site:manageblocks');
563 $this->assertEquals(array_values($expectedcaps), array_values($actualcaps));
566 public function test_other_editing_caps() {
567 $this->testpage
->set_context(context_system
::instance());
568 $this->setAdminUser();
571 $this->testpage
->set_other_editing_capability(array('moodle/course:manageactivities', 'moodle/site:other'));
573 $actualcaps = $this->testpage
->all_editing_caps();
574 $expectedcaps = array('moodle/course:manageactivities', 'moodle/site:other', 'moodle/site:manageblocks');
575 $this->assertEquals(array_values($expectedcaps), array_values($actualcaps));
579 * Test getting a renderer.
581 public function test_get_renderer() {
582 global $OUTPUT, $PAGE;
583 $oldoutput = $OUTPUT;
585 $PAGE = $this->testpage
;
587 $this->testpage
->set_pagelayout('standard');
588 $this->assertEquals('standard', $this->testpage
->pagelayout
);
589 // Initialise theme and output for the next tests.
590 $this->testpage
->initialise_theme_and_output();
591 // Check the generated $OUTPUT object is a core renderer.
592 $this->assertInstanceOf('core_renderer', $OUTPUT);
593 // Check we can get a core renderer if we explicitly request one (no component).
594 $this->assertInstanceOf('core_renderer', $this->testpage
->get_renderer('core'));
595 // Check we get a CLI renderer if we request a maintenance renderer. The CLI target should take precedence.
596 $this->assertInstanceOf('core_renderer_cli',
597 $this->testpage
->get_renderer('core', null, RENDERER_TARGET_MAINTENANCE
));
599 // Check we can get a coures renderer if we explicitly request one (valid component).
600 $this->assertInstanceOf('core_course_renderer', $this->testpage
->get_renderer('core', 'course'));
602 // Check a properly invalid component.
604 $this->testpage
->get_renderer('core', 'monkeys');
605 $this->fail('Request for renderer with invalid component didn\'t throw expected exception.');
606 } catch (coding_exception
$exception) {
607 $this->assertEquals('monkeys', $exception->debuginfo
);
611 $OUTPUT = $oldoutput;
615 * Tests getting a renderer with a maintenance layout.
617 * This layout has special hacks in place in order to deliver a "maintenance" renderer.
619 public function test_get_renderer_maintenance() {
620 global $OUTPUT, $PAGE;
621 $oldoutput = $OUTPUT;
623 $PAGE = $this->testpage
;
625 $this->testpage
->set_pagelayout('maintenance');
626 $this->assertEquals('maintenance', $this->testpage
->pagelayout
);
627 // Initialise theme and output for the next tests.
628 $this->testpage
->initialise_theme_and_output();
629 // Check the generated $OUTPUT object is a core cli renderer.
630 // It shouldn't be maintenance because there the cli target should take greater precedence.
631 $this->assertInstanceOf('core_renderer_cli', $OUTPUT);
632 // Check we can get a core renderer if we explicitly request one (no component).
633 $this->assertInstanceOf('core_renderer', $this->testpage
->get_renderer('core'));
634 // Check we get a CLI renderer if we request a maintenance renderer. The CLI target should take precedence.
635 $this->assertInstanceOf('core_renderer_cli',
636 $this->testpage
->get_renderer('core', null, RENDERER_TARGET_MAINTENANCE
));
637 // Check we can get a coures renderer if we explicitly request one (valid component).
638 $this->assertInstanceOf('core_course_renderer', $this->testpage
->get_renderer('core', 'course'));
641 $this->testpage
->get_renderer('core', 'monkeys');
642 $this->fail('Request for renderer with invalid component didn\'t throw expected exception.');
643 } catch (coding_exception
$exception) {
644 $this->assertEquals('monkeys', $exception->debuginfo
);
648 $OUTPUT = $oldoutput;
653 * Test-specific subclass to make some protected things public.
655 class testable_moodle_page
extends moodle_page
{
656 public function initialise_default_pagetype($script = null) {
657 parent
::initialise_default_pagetype($script);
659 public function url_to_class_name($url) {
660 return parent
::url_to_class_name($url);
662 public function all_editing_caps() {
663 return parent
::all_editing_caps();