MDL-37574 - Fix navigation tests: remove title
[moodle.git] / lib / tests / navigationlib_test.php
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/>.
17 /**
18  * Unit tests for lib/navigationlib.php
19  *
20  * @package   core
21  * @category  phpunit
22  * @copyright 2009 Sam Hemelryk
23  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later (5)
24  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->libdir . '/navigationlib.php');
32 class navigation_node_testcase extends basic_testcase {
33     protected $tree;
34     protected $fakeproperties = array(
35         'text' => 'text',
36         'shorttext' => 'A very silly extra long short text string, more than 25 characters',
37         'key' => 'key',
38         'type' => 'navigation_node::TYPE_COURSE',
39         'action' => 'http://www.moodle.org/');
40     protected $activeurl = null;
41     protected $inactivenode = null;
43     /**
44      * @var navigation_node
45      */
46     public $node;
48     protected function setUp() {
49         global $CFG, $PAGE, $SITE;
50         parent::setUp();
52         $PAGE->set_url('/');
53         $PAGE->set_course($SITE);
55         $this->activeurl = $PAGE->url;
56         navigation_node::override_active_url($this->activeurl);
58         $this->inactiveurl = new moodle_url('http://www.moodle.com/');
59         $this->fakeproperties['action'] = $this->inactiveurl;
61         $this->node = new navigation_node('Test Node');
62         $this->node->type = navigation_node::TYPE_SYSTEM;
63         $demo1 = $this->node->add('demo1', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'demo1', new pix_icon('i/course', ''));
64         $demo2 = $this->node->add('demo2', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'demo2', new pix_icon('i/course', ''));
65         $demo3 = $this->node->add('demo3', $this->inactiveurl, navigation_node::TYPE_CATEGORY, null, 'demo3',new pix_icon('i/course', ''));
66         $demo4 = $demo3->add('demo4', $this->inactiveurl,navigation_node::TYPE_COURSE,  null, 'demo4', new pix_icon('i/course', ''));
67         $demo5 = $demo3->add('demo5', $this->activeurl, navigation_node::TYPE_COURSE, null, 'demo5',new pix_icon('i/course', ''));
68         $demo5->add('activity1', null, navigation_node::TYPE_ACTIVITY, null, 'activity1')->make_active();
69         $hiddendemo1 = $this->node->add('hiddendemo1', $this->inactiveurl, navigation_node::TYPE_CATEGORY, null, 'hiddendemo1', new pix_icon('i/course', ''));
70         $hiddendemo1->hidden = true;
71         $hiddendemo1->add('hiddendemo2', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'hiddendemo2', new pix_icon('i/course', ''))->helpbutton = 'Here is a help button';
72         $hiddendemo1->add('hiddendemo3', $this->inactiveurl, navigation_node::TYPE_COURSE,null, 'hiddendemo3', new pix_icon('i/course', ''))->display = false;
73     }
75     public function test___construct() {
76         global $CFG;
77         $node = new navigation_node($this->fakeproperties);
78         $this->assertEquals($node->text, $this->fakeproperties['text']);
79         $this->assertTrue(strpos($this->fakeproperties['shorttext'], substr($node->shorttext,0, -3))===0);
80         $this->assertEquals($node->key, $this->fakeproperties['key']);
81         $this->assertEquals($node->type, $this->fakeproperties['type']);
82         $this->assertEquals($node->action, $this->fakeproperties['action']);
83     }
84     public function test_add() {
85         global $CFG;
86         // Add a node with all args set
87         $node1 = $this->node->add('test_add_1','http://www.moodle.org/',navigation_node::TYPE_COURSE,'testadd1','key',new pix_icon('i/course', ''));
88         // Add a node with the minimum args required
89         $node2 = $this->node->add('test_add_2',null, navigation_node::TYPE_CUSTOM,'testadd2');
90         $node3 = $this->node->add(str_repeat('moodle ', 15),str_repeat('moodle', 15));
92         $this->assertInstanceOf('navigation_node', $node1);
93         $this->assertInstanceOf('navigation_node', $node2);
94         $this->assertInstanceOf('navigation_node', $node3);
96         $ref = $this->node->get('key');
97         $this->assertSame($node1, $ref);
99         $ref = $this->node->get($node2->key);
100         $this->assertSame($node2, $ref);
102         $ref = $this->node->get($node2->key, $node2->type);
103         $this->assertSame($node2, $ref);
105         $ref = $this->node->get($node3->key, $node3->type);
106         $this->assertSame($node3, $ref);
107     }
109     public function test_add_before() {
110         global $CFG;
111         // Create 3 nodes
112         $node1 = navigation_node::create('test_add_1', null, navigation_node::TYPE_CUSTOM,
113             'test 1', 'testadd1');
114         $node2 = navigation_node::create('test_add_2', null, navigation_node::TYPE_CUSTOM,
115             'test 2', 'testadd2');
116         $node3 = navigation_node::create('test_add_3', null, navigation_node::TYPE_CUSTOM,
117             'test 3', 'testadd3');
118         // Add node 2, then node 1 before 2, then node 3 at end
119         $this->node->add_node($node2);
120         $this->node->add_node($node1, 'testadd2');
121         $this->node->add_node($node3);
122         // Check the last 3 nodes are in 1, 2, 3 order and have those indexes
123         foreach($this->node->children as $child) {
124             $keys[] = $child->key;
125         }
126         $this->assertEquals('testadd1', $keys[count($keys)-3]);
127         $this->assertEquals('testadd2', $keys[count($keys)-2]);
128         $this->assertEquals('testadd3', $keys[count($keys)-1]);
129     }
131     public function test_add_class() {
132         $node = $this->node->get('demo1');
133         $this->assertInstanceOf('navigation_node', $node);
134         if ($node !== false) {
135             $node->add_class('myclass');
136             $classes = $node->classes;
137             $this->assertTrue(in_array('myclass', $classes));
138         }
139     }
142     public function test_check_if_active() {
143         // First test the string urls
144         // demo1 -> action is http://www.moodle.org/, thus should be true
145         $demo5 = $this->node->find('demo5', navigation_node::TYPE_COURSE);
146         if ($this->assertInstanceOf('navigation_node', $demo5)) {
147             $this->assertTrue($demo5->check_if_active());
148         }
150         // demo2 -> action is http://www.moodle.com/, thus should be false
151         $demo2 = $this->node->get('demo2');
152         if ($this->assertInstanceOf('navigation_node', $demo2)) {
153             $this->assertFalse($demo2->check_if_active());
154         }
155     }
157     public function test_contains_active_node() {
158         // demo5, and activity1 were set to active during setup
159         // Should be true as it contains all nodes
160         $this->assertTrue($this->node->contains_active_node());
161         // Should be true as demo5 is a child of demo3
162         $this->assertTrue($this->node->get('demo3')->contains_active_node());
163         // Obviously duff
164         $this->assertFalse($this->node->get('demo1')->contains_active_node());
165         // Should be true as demo5 contains activity1
166         $this->assertTrue($this->node->get('demo3')->get('demo5')->contains_active_node());
167         // Should be true activity1 is the active node
168         $this->assertTrue($this->node->get('demo3')->get('demo5')->get('activity1')->contains_active_node());
169         // Obviously duff
170         $this->assertFalse($this->node->get('demo3')->get('demo4')->contains_active_node());
171     }
173     public function test_find_active_node() {
174         $activenode1 = $this->node->find_active_node();
175         $activenode2 = $this->node->get('demo1')->find_active_node();
177         if ($this->assertInstanceOf('navigation_node', $activenode1)) {
178             $ref = $this->node->get('demo3')->get('demo5')->get('activity1');
179             $this->assertSame($activenode1, $ref);
180         }
182         $this->assertNotInstanceOf('navigation_node', $activenode2);
183     }
185     public function test_find() {
186         $node1 = $this->node->find('demo1', navigation_node::TYPE_COURSE);
187         $node2 = $this->node->find('demo5', navigation_node::TYPE_COURSE);
188         $node3 = $this->node->find('demo5', navigation_node::TYPE_CATEGORY);
189         $node4 = $this->node->find('demo0', navigation_node::TYPE_COURSE);
190         $this->assertInstanceOf('navigation_node', $node1);
191         $this->assertInstanceOf('navigation_node', $node2);
192         $this->assertNotInstanceOf('navigation_node', $node3);
193         $this->assertNotInstanceOf('navigation_node', $node4);
194     }
196     public function test_find_expandable() {
197         $expandable = array();
198         $this->node->find_expandable($expandable);
199         //TODO: find out what is wrong here - it was returning 4 before the conversion
200         //$this->assertEquals(count($expandable), 4);
201         $this->assertEquals(count($expandable), 0);
202         if (count($expandable) === 4) {
203             $name = $expandable[0]['key'];
204             $name .= $expandable[1]['key'];
205             $name .= $expandable[2]['key'];
206             $name .= $expandable[3]['key'];
207             $this->assertEquals($name, 'demo1demo2demo4hiddendemo2');
208         }
209     }
211     public function test_get() {
212         $node1 = $this->node->get('demo1'); // Exists
213         $node2 = $this->node->get('demo4'); // Doesn't exist for this node
214         $node3 = $this->node->get('demo0'); // Doesn't exist at all
215         $node4 = $this->node->get(false);   // Sometimes occurs in nature code
216         $this->assertInstanceOf('navigation_node', $node1);
217         $this->assertFalse($node2);
218         $this->assertFalse($node3);
219         $this->assertFalse($node4);
220     }
222     public function test_get_css_type() {
223         $csstype1 = $this->node->get('demo3')->get_css_type();
224         $csstype2 = $this->node->get('demo3')->get('demo5')->get_css_type();
225         $this->node->get('demo3')->get('demo5')->type = 1000;
226         $csstype3 = $this->node->get('demo3')->get('demo5')->get_css_type();
227         $this->assertEquals($csstype1, 'type_category');
228         $this->assertEquals($csstype2, 'type_course');
229         $this->assertEquals($csstype3, 'type_unknown');
230     }
232     public function test_make_active() {
233         global $CFG;
234         $node1 = $this->node->add('active node 1', null, navigation_node::TYPE_CUSTOM, null, 'anode1');
235         $node2 = $this->node->add('active node 2', new moodle_url($CFG->wwwroot), navigation_node::TYPE_COURSE, null, 'anode2');
236         $node1->make_active();
237         $this->node->get('anode2')->make_active();
238         $this->assertTrue($node1->isactive);
239         $this->assertTrue($this->node->get('anode2')->isactive);
240     }
241     public function test_remove() {
242         $remove1 = $this->node->add('child to remove 1', null, navigation_node::TYPE_CUSTOM, null, 'remove1');
243         $remove2 = $this->node->add('child to remove 2', null, navigation_node::TYPE_CUSTOM, null, 'remove2');
244         $remove3 = $remove2->add('child to remove 3', null, navigation_node::TYPE_CUSTOM, null, 'remove3');
246         $this->assertInstanceOf('navigation_node', $remove1);
247         $this->assertInstanceOf('navigation_node', $remove2);
248         $this->assertInstanceOf('navigation_node', $remove3);
250         $this->assertInstanceOf('navigation_node', $this->node->get('remove1'));
251         $this->assertInstanceOf('navigation_node', $this->node->get('remove2'));
252         $this->assertInstanceOf('navigation_node', $remove2->get('remove3'));
254         $this->assertTrue($remove1->remove());
255         $this->assertTrue($this->node->get('remove2')->remove());
256         $this->assertTrue($remove2->get('remove3')->remove());
258         $this->assertFalse($this->node->get('remove1'));
259         $this->assertFalse($this->node->get('remove2'));
260     }
261     public function test_remove_class() {
262         $this->node->add_class('testclass');
263         $this->assertTrue($this->node->remove_class('testclass'));
264         $this->assertFalse(in_array('testclass', $this->node->classes));
265     }
268 /**
269  * This is a dummy object that allows us to call protected methods within the
270  * global navigation class by prefixing the methods with `exposed_`
271  */
272 class exposed_global_navigation extends global_navigation {
273     protected $exposedkey = 'exposed_';
274     public function __construct(moodle_page $page=null) {
275         global $PAGE;
276         if ($page === null) {
277             $page = $PAGE;
278         }
279         parent::__construct($page);
280         $this->cache = new navigation_cache('unittest_nav');
281     }
282     public function __call($method, $arguments) {
283         if (strpos($method,$this->exposedkey) !== false) {
284             $method = substr($method, strlen($this->exposedkey));
285         }
286         if (method_exists($this, $method)) {
287             return call_user_func_array(array($this, $method), $arguments);
288         }
289         throw new coding_exception('You have attempted to access a method that does not exist for the given object '.$method, DEBUG_DEVELOPER);
290     }
291     public function set_initialised() {
292         $this->initialised = true;
293     }
296 class mock_initialise_global_navigation extends global_navigation {
298     static $count = 1;
300     public function load_for_category() {
301         $this->add('load_for_category', null, null, null, 'initcall'.self::$count);
302         self::$count++;
303         return 0;
304     }
306     public function load_for_course() {
307         $this->add('load_for_course', null, null, null, 'initcall'.self::$count);
308         self::$count++;
309         return 0;
310     }
312     public function load_for_activity() {
313         $this->add('load_for_activity', null, null, null, 'initcall'.self::$count);
314         self::$count++;
315         return 0;
316     }
318     public function load_for_user($user=null, $forceforcontext=false) {
319         $this->add('load_for_user', null, null, null, 'initcall'.self::$count);
320         self::$count++;
321         return 0;
322     }
325 class global_navigation_testcase extends basic_testcase {
326     /**
327      * @var global_navigation
328      */
329     public $node;
331     protected function setUp() {
332         parent::setUp();
334         $this->node = new exposed_global_navigation();
335         // Create an initial tree structure to work with
336         $cat1 = $this->node->add('category 1', null, navigation_node::TYPE_CATEGORY, null, 'cat1');
337         $cat2 = $this->node->add('category 2', null, navigation_node::TYPE_CATEGORY, null, 'cat2');
338         $cat3 = $this->node->add('category 3', null, navigation_node::TYPE_CATEGORY, null, 'cat3');
339         $sub1 = $cat2->add('sub category 1', null, navigation_node::TYPE_CATEGORY, null, 'sub1');
340         $sub2 = $cat2->add('sub category 2', null, navigation_node::TYPE_CATEGORY, null, 'sub2');
341         $sub3 = $cat2->add('sub category 3', null, navigation_node::TYPE_CATEGORY, null, 'sub3');
342         $course1 = $sub2->add('course 1', null, navigation_node::TYPE_COURSE, null, 'course1');
343         $course2 = $sub2->add('course 2', null, navigation_node::TYPE_COURSE, null, 'course2');
344         $course3 = $sub2->add('course 3', null, navigation_node::TYPE_COURSE, null, 'course3');
345         $section1 = $course2->add('section 1', null, navigation_node::TYPE_SECTION, null, 'sec1');
346         $section2 = $course2->add('section 2', null, navigation_node::TYPE_SECTION, null, 'sec2');
347         $section3 = $course2->add('section 3', null, navigation_node::TYPE_SECTION, null, 'sec3');
348         $act1 = $section2->add('activity 1', null, navigation_node::TYPE_ACTIVITY, null, 'act1');
349         $act2 = $section2->add('activity 2', null, navigation_node::TYPE_ACTIVITY, null, 'act2');
350         $act3 = $section2->add('activity 3', null, navigation_node::TYPE_ACTIVITY, null, 'act3');
351         $res1 = $section2->add('resource 1', null, navigation_node::TYPE_RESOURCE, null, 'res1');
352         $res2 = $section2->add('resource 2', null, navigation_node::TYPE_RESOURCE, null, 'res2');
353         $res3 = $section2->add('resource 3', null, navigation_node::TYPE_RESOURCE, null, 'res3');
354     }
356     public function test_module_extends_navigation() {
357         $this->assertTrue($this->node->exposed_module_extends_navigation('data'));
358         $this->assertFalse($this->node->exposed_module_extends_navigation('test1'));
359     }
362 /**
363  * This is a dummy object that allows us to call protected methods within the
364  * global navigation class by prefixing the methods with `exposed_`
365  */
366 class exposed_navbar extends navbar {
367     protected $exposedkey = 'exposed_';
368     public function __construct(moodle_page $page) {
369         parent::__construct($page);
370         $this->cache = new navigation_cache('unittest_nav');
371     }
372     function __call($method, $arguments) {
373         if (strpos($method,$this->exposedkey) !== false) {
374             $method = substr($method, strlen($this->exposedkey));
375         }
376         if (method_exists($this, $method)) {
377             return call_user_func_array(array($this, $method), $arguments);
378         }
379         throw new coding_exception('You have attempted to access a method that does not exist for the given object '.$method, DEBUG_DEVELOPER);
380     }
383 class navigation_exposed_moodle_page extends moodle_page {
384     public function set_navigation(navigation_node $node) {
385         $this->_navigation = $node;
386     }
389 class navbar_testcase extends advanced_testcase {
390     protected $node;
391     protected $oldnav;
393     protected function setUp() {
394         global $PAGE, $SITE;
395         parent::setUp();
397         $this->resetAfterTest(true);
399         $PAGE->set_url('/');
400         $PAGE->set_course($SITE);
402         $tempnode = new exposed_global_navigation();
403         // Create an initial tree structure to work with
404         $cat1 = $tempnode->add('category 1', null, navigation_node::TYPE_CATEGORY, null, 'cat1');
405         $cat2 = $tempnode->add('category 2', null, navigation_node::TYPE_CATEGORY, null, 'cat2');
406         $cat3 = $tempnode->add('category 3', null, navigation_node::TYPE_CATEGORY, null, 'cat3');
407         $sub1 = $cat2->add('sub category 1', null, navigation_node::TYPE_CATEGORY, null, 'sub1');
408         $sub2 = $cat2->add('sub category 2', null, navigation_node::TYPE_CATEGORY, null, 'sub2');
409         $sub3 = $cat2->add('sub category 3', null, navigation_node::TYPE_CATEGORY, null, 'sub3');
410         $course1 = $sub2->add('course 1', null, navigation_node::TYPE_COURSE, null, 'course1');
411         $course2 = $sub2->add('course 2', null, navigation_node::TYPE_COURSE, null, 'course2');
412         $course3 = $sub2->add('course 3', null, navigation_node::TYPE_COURSE, null, 'course3');
413         $section1 = $course2->add('section 1', null, navigation_node::TYPE_SECTION, null, 'sec1');
414         $section2 = $course2->add('section 2', null, navigation_node::TYPE_SECTION, null, 'sec2');
415         $section3 = $course2->add('section 3', null, navigation_node::TYPE_SECTION, null, 'sec3');
416         $act1 = $section2->add('activity 1', null, navigation_node::TYPE_ACTIVITY, null, 'act1');
417         $act2 = $section2->add('activity 2', null, navigation_node::TYPE_ACTIVITY, null, 'act2');
418         $act3 = $section2->add('activity 3', null, navigation_node::TYPE_ACTIVITY, null, 'act3');
419         $res1 = $section2->add('resource 1', null, navigation_node::TYPE_RESOURCE, null, 'res1');
420         $res2 = $section2->add('resource 2', null, navigation_node::TYPE_RESOURCE, null, 'res2');
421         $res3 = $section2->add('resource 3', null, navigation_node::TYPE_RESOURCE, null, 'res3');
422         $tempnode->find('course2', navigation_node::TYPE_COURSE)->make_active();
424         $page = new navigation_exposed_moodle_page();
425         $page->set_url($PAGE->url);
426         $page->set_context($PAGE->context);
428         $navigation = new exposed_global_navigation($page);
429         $navigation->children = $tempnode->children;
430         $navigation->set_initialised();
431         $page->set_navigation($navigation);
433         $this->cache = new navigation_cache('unittest_nav');
434         $this->node = new exposed_navbar($page);
435     }
436     public function test_add() {
437         // Add a node with all args set
438         $this->node->add('test_add_1','http://www.moodle.org/',navigation_node::TYPE_COURSE,'testadd1','testadd1',new pix_icon('i/course', ''));
439         // Add a node with the minimum args required
440         $this->node->add('test_add_2','http://www.moodle.org/',navigation_node::TYPE_COURSE,'testadd2','testadd2',new pix_icon('i/course', ''));
441         $this->assertInstanceOf('navigation_node', $this->node->get('testadd1'));
442         $this->assertInstanceOf('navigation_node', $this->node->get('testadd2'));
443     }
444     public function test_has_items() {
445         $this->assertTrue($this->node->has_items());
446     }
449 class navigation_cache_testcase extends basic_testcase {
450     protected $cache;
452     protected function setUp() {
453         parent::setUp();
455         $this->cache = new navigation_cache('unittest_nav');
456         $this->cache->anysetvariable = true;
457     }
458     public function test___get() {
459         $this->assertTrue($this->cache->anysetvariable);
460         $this->assertEquals($this->cache->notasetvariable, null);
461     }
462     public function test___set() {
463         $this->cache->myname = 'Sam Hemelryk';
464         $this->assertTrue($this->cache->cached('myname'));
465         $this->assertEquals($this->cache->myname, 'Sam Hemelryk');
466     }
467     public function test_cached() {
468         $this->assertTrue($this->cache->cached('anysetvariable'));
469         $this->assertFalse($this->cache->cached('notasetvariable'));
470     }
471     public function test_clear() {
472         $cache = clone($this->cache);
473         $this->assertTrue($cache->cached('anysetvariable'));
474         $cache->clear();
475         $this->assertFalse($cache->cached('anysetvariable'));
476     }
477     public function test_set() {
478         $this->cache->set('software', 'Moodle');
479         $this->assertTrue($this->cache->cached('software'));
480         $this->assertEquals($this->cache->software, 'Moodle');
481     }
484 /**
485  * This is a dummy object that allows us to call protected methods within the
486  * global navigation class by prefixing the methods with `exposed_`
487  */
488 class exposed_settings_navigation extends settings_navigation {
489     protected $exposedkey = 'exposed_';
490     function __construct() {
491         global $PAGE;
492         parent::__construct($PAGE);
493         $this->cache = new navigation_cache('unittest_nav');
494     }
495     function __call($method, $arguments) {
496         if (strpos($method,$this->exposedkey) !== false) {
497             $method = substr($method, strlen($this->exposedkey));
498         }
499         if (method_exists($this, $method)) {
500             return call_user_func_array(array($this, $method), $arguments);
501         }
502         throw new coding_exception('You have attempted to access a method that does not exist for the given object '.$method, DEBUG_DEVELOPER);
503     }
506 class settings_navigation_testcase extends advanced_testcase {
507     protected $node;
508     protected $cache;
510     protected function setUp() {
511         global $PAGE, $SITE;
512         parent::setUp();
514         $this->resetAfterTest(true);
516         $PAGE->set_url('/');
517         $PAGE->set_course($SITE);
519         $this->cache = new navigation_cache('unittest_nav');
520         $this->node = new exposed_settings_navigation();
521     }
522     public function test___construct() {
523         $this->node = new exposed_settings_navigation();
524     }
525     public function test___initialise() {
526         $this->node->initialise();
527         $this->assertEquals($this->node->id, 'settingsnav');
528     }
529     public function test_in_alternative_role() {
530         $this->assertFalse($this->node->exposed_in_alternative_role());
531     }