See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core; 18 19 use action_link; 20 use global_navigation; 21 use navbar; 22 use navigation_cache; 23 use navigation_node; 24 use navigation_node_collection; 25 use pix_icon; 26 use popup_action; 27 use settings_navigation; 28 29 defined('MOODLE_INTERNAL') || die(); 30 31 global $CFG; 32 require_once($CFG->libdir . '/navigationlib.php'); 33 34 /** 35 * Unit tests for lib/navigationlib.php 36 * 37 * @package core 38 * @category test 39 * @copyright 2009 Sam Hemelryk 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later (5) 41 */ 42 class navigationlib_test extends \advanced_testcase { 43 /** 44 * @var navigation_node 45 */ 46 public $node; 47 48 protected function setup_node() { 49 global $PAGE, $SITE; 50 51 $PAGE->set_url('/'); 52 $PAGE->set_course($SITE); 53 54 $activeurl = $PAGE->url; 55 $inactiveurl = new \moodle_url('http://www.moodle.com/'); 56 57 navigation_node::override_active_url($PAGE->url); 58 59 $this->node = new navigation_node('Test Node'); 60 $this->node->type = navigation_node::TYPE_SYSTEM; 61 // We add the first child without key. This way we make sure all keys search by comparison is performed using ===. 62 $this->node->add('first child without key', null, navigation_node::TYPE_CUSTOM); 63 $demo1 = $this->node->add('demo1', $inactiveurl, navigation_node::TYPE_COURSE, null, 'demo1', new pix_icon('i/course', '')); 64 $demo2 = $this->node->add('demo2', $inactiveurl, navigation_node::TYPE_COURSE, null, 'demo2', new pix_icon('i/course', '')); 65 $demo3 = $this->node->add('demo3', $inactiveurl, navigation_node::TYPE_CATEGORY, null, 'demo3', new pix_icon('i/course', '')); 66 $demo4 = $demo3->add('demo4', $inactiveurl, navigation_node::TYPE_COURSE, null, 'demo4', new pix_icon('i/course', '')); 67 $demo5 = $demo3->add('demo5', $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 $demo6 = $demo3->add('demo6', null, navigation_node::TYPE_CONTAINER, 'container node test', 'demo6'); 70 $hiddendemo1 = $this->node->add('hiddendemo1', $inactiveurl, navigation_node::TYPE_CATEGORY, null, 'hiddendemo1', new pix_icon('i/course', '')); 71 $hiddendemo1->hidden = true; 72 $hiddendemo1->add('hiddendemo2', $inactiveurl, navigation_node::TYPE_COURSE, null, 'hiddendemo2', new pix_icon('i/course', ''))->helpbutton = 'Here is a help button'; 73 $hiddendemo1->add('hiddendemo3', $inactiveurl, navigation_node::TYPE_COURSE, null, 'hiddendemo3', new pix_icon('i/course', ''))->display = false; 74 } 75 76 public function test_node__construct() { 77 $this->setup_node(); 78 79 $fakeproperties = array( 80 'text' => 'text', 81 'shorttext' => 'A very silly extra long short text string, more than 25 characters', 82 'key' => 'key', 83 'type' => 'navigation_node::TYPE_COURSE', 84 'action' => new \moodle_url('http://www.moodle.org/')); 85 86 $node = new navigation_node($fakeproperties); 87 $this->assertSame($fakeproperties['text'], $node->text); 88 $this->assertTrue(strpos($fakeproperties['shorttext'], substr($node->shorttext, 0, -3)) === 0); 89 $this->assertSame($fakeproperties['key'], $node->key); 90 $this->assertSame($fakeproperties['type'], $node->type); 91 $this->assertSame($fakeproperties['action'], $node->action); 92 } 93 94 public function test_node_add() { 95 $this->setup_node(); 96 97 // Add a node with all args set. 98 $node1 = $this->node->add('test_add_1', 'http://www.moodle.org/', navigation_node::TYPE_COURSE, 'testadd1', 'key', new pix_icon('i/course', '')); 99 // Add a node with the minimum args required. 100 $node2 = $this->node->add('test_add_2', null, navigation_node::TYPE_CUSTOM, 'testadd2'); 101 $node3 = $this->node->add(str_repeat('moodle ', 15), str_repeat('moodle', 15)); 102 103 $this->assertInstanceOf('navigation_node', $node1); 104 $this->assertInstanceOf('navigation_node', $node2); 105 $this->assertInstanceOf('navigation_node', $node3); 106 107 $ref = $this->node->get('key'); 108 $this->assertSame($node1, $ref); 109 110 $ref = $this->node->get($node2->key); 111 $this->assertSame($node2, $ref); 112 113 $ref = $this->node->get($node2->key, $node2->type); 114 $this->assertSame($node2, $ref); 115 116 $ref = $this->node->get($node3->key, $node3->type); 117 $this->assertSame($node3, $ref); 118 } 119 120 public function test_node_add_before() { 121 $this->setup_node(); 122 123 // Create 3 nodes. 124 $node1 = navigation_node::create('test_add_1', null, navigation_node::TYPE_CUSTOM, 125 'test 1', 'testadd1'); 126 $node2 = navigation_node::create('test_add_2', null, navigation_node::TYPE_CUSTOM, 127 'test 2', 'testadd2'); 128 $node3 = navigation_node::create('test_add_3', null, navigation_node::TYPE_CUSTOM, 129 'test 3', 'testadd3'); 130 // Add node 2, then node 1 before 2, then node 3 at end. 131 $this->node->add_node($node2); 132 $this->node->add_node($node1, 'testadd2'); 133 $this->node->add_node($node3); 134 // Check the last 3 nodes are in 1, 2, 3 order and have those indexes. 135 foreach ($this->node->children as $child) { 136 $keys[] = $child->key; 137 } 138 $this->assertSame('testadd1', $keys[count($keys)-3]); 139 $this->assertSame('testadd2', $keys[count($keys)-2]); 140 $this->assertSame('testadd3', $keys[count($keys)-1]); 141 } 142 143 public function test_node_add_class() { 144 $this->setup_node(); 145 146 $node = $this->node->get('demo1'); 147 $this->assertInstanceOf('navigation_node', $node); 148 if ($node !== false) { 149 $node->add_class('myclass'); 150 $classes = $node->classes; 151 $this->assertContains('myclass', $classes); 152 } 153 } 154 155 public function test_node_check_if_active() { 156 $this->setup_node(); 157 158 // First test the string urls 159 // Demo1 -> action is http://www.moodle.org/, thus should be true. 160 $demo5 = $this->node->find('demo5', navigation_node::TYPE_COURSE); 161 if ($this->assertInstanceOf('navigation_node', $demo5)) { 162 $this->assertTrue($demo5->check_if_active()); 163 } 164 165 // Demo2 -> action is http://www.moodle.com/, thus should be false. 166 $demo2 = $this->node->get('demo2'); 167 if ($this->assertInstanceOf('navigation_node', $demo2)) { 168 $this->assertFalse($demo2->check_if_active()); 169 } 170 } 171 172 public function test_node_contains_active_node() { 173 $this->setup_node(); 174 175 // Demo5, and activity1 were set to active during setup. 176 // Should be true as it contains all nodes. 177 $this->assertTrue($this->node->contains_active_node()); 178 // Should be true as demo5 is a child of demo3. 179 $this->assertTrue($this->node->get('demo3')->contains_active_node()); 180 // Obviously duff. 181 $this->assertFalse($this->node->get('demo1')->contains_active_node()); 182 // Should be true as demo5 contains activity1. 183 $this->assertTrue($this->node->get('demo3')->get('demo5')->contains_active_node()); 184 // Should be true activity1 is the active node. 185 $this->assertTrue($this->node->get('demo3')->get('demo5')->get('activity1')->contains_active_node()); 186 // Obviously duff. 187 $this->assertFalse($this->node->get('demo3')->get('demo4')->contains_active_node()); 188 } 189 190 public function test_node_find_active_node() { 191 $this->setup_node(); 192 193 $activenode1 = $this->node->find_active_node(); 194 $activenode2 = $this->node->get('demo1')->find_active_node(); 195 196 if ($this->assertInstanceOf('navigation_node', $activenode1)) { 197 $ref = $this->node->get('demo3')->get('demo5')->get('activity1'); 198 $this->assertSame($activenode1, $ref); 199 } 200 201 $this->assertNotInstanceOf('navigation_node', $activenode2); 202 } 203 204 public function test_node_find() { 205 $this->setup_node(); 206 207 $node1 = $this->node->find('demo1', navigation_node::TYPE_COURSE); 208 $node2 = $this->node->find('demo5', navigation_node::TYPE_COURSE); 209 $node3 = $this->node->find('demo5', navigation_node::TYPE_CATEGORY); 210 $node4 = $this->node->find('demo0', navigation_node::TYPE_COURSE); 211 $this->assertInstanceOf('navigation_node', $node1); 212 $this->assertInstanceOf('navigation_node', $node2); 213 $this->assertNotInstanceOf('navigation_node', $node3); 214 $this->assertNotInstanceOf('navigation_node', $node4); 215 } 216 217 public function test_node_find_expandable() { 218 $this->setup_node(); 219 220 $expandable = array(); 221 $this->node->find_expandable($expandable); 222 223 $this->assertCount(0, $expandable); 224 if (count($expandable) === 4) { 225 $name = $expandable[0]['key']; 226 $name .= $expandable[1]['key']; 227 $name .= $expandable[2]['key']; 228 $name .= $expandable[3]['key']; 229 $this->assertSame('demo1demo2demo4hiddendemo2', $name); 230 } 231 } 232 233 public function test_node_get() { 234 $this->setup_node(); 235 236 $node1 = $this->node->get('demo1'); // Exists. 237 $node2 = $this->node->get('demo4'); // Doesn't exist for this node. 238 $node3 = $this->node->get('demo0'); // Doesn't exist at all. 239 $node4 = $this->node->get(false); // Sometimes occurs in nature code. 240 $this->assertInstanceOf('navigation_node', $node1); 241 $this->assertFalse($node2); 242 $this->assertFalse($node3); 243 $this->assertFalse($node4); 244 } 245 246 public function test_node_get_css_type() { 247 $this->setup_node(); 248 249 $csstype1 = $this->node->get('demo3')->get_css_type(); 250 $csstype2 = $this->node->get('demo3')->get('demo5')->get_css_type(); 251 $this->node->get('demo3')->get('demo5')->type = 1000; 252 $csstype3 = $this->node->get('demo3')->get('demo5')->get_css_type(); 253 $csstype4 = $this->node->get('demo3')->get('demo6')->get_css_type(); 254 $this->assertSame('type_category', $csstype1); 255 $this->assertSame('type_course', $csstype2); 256 $this->assertSame('type_unknown', $csstype3); 257 $this->assertSame('type_container', $csstype4); 258 } 259 260 public function test_node_make_active() { 261 global $CFG; 262 $this->setup_node(); 263 264 $node1 = $this->node->add('active node 1', null, navigation_node::TYPE_CUSTOM, null, 'anode1'); 265 $node2 = $this->node->add('active node 2', new \moodle_url($CFG->wwwroot), navigation_node::TYPE_COURSE, null, 'anode2'); 266 $node1->make_active(); 267 $this->node->get('anode2')->make_active(); 268 $this->assertTrue($node1->isactive); 269 $this->assertTrue($this->node->get('anode2')->isactive); 270 } 271 272 public function test_node_remove() { 273 $this->setup_node(); 274 275 $remove1 = $this->node->add('child to remove 1', null, navigation_node::TYPE_CUSTOM, null, 'remove1'); 276 $remove2 = $this->node->add('child to remove 2', null, navigation_node::TYPE_CUSTOM, null, 'remove2'); 277 $remove3 = $remove2->add('child to remove 3', null, navigation_node::TYPE_CUSTOM, null, 'remove3'); 278 279 $this->assertInstanceOf('navigation_node', $remove1); 280 $this->assertInstanceOf('navigation_node', $remove2); 281 $this->assertInstanceOf('navigation_node', $remove3); 282 283 $this->assertInstanceOf('navigation_node', $this->node->get('remove1')); 284 $this->assertInstanceOf('navigation_node', $this->node->get('remove2')); 285 $this->assertInstanceOf('navigation_node', $remove2->get('remove3')); 286 287 // Remove element and make sure this is no longer a child. 288 $this->assertTrue($remove1->remove()); 289 $this->assertFalse($this->node->get('remove1')); 290 $this->assertFalse(in_array('remove1', $this->node->get_children_key_list(), true)); 291 292 // Make sure that we can insert element after removal. 293 $insertelement = navigation_node::create('extra element 4', null, navigation_node::TYPE_CUSTOM, null, 'element4'); 294 $this->node->add_node($insertelement, 'remove2'); 295 $this->assertNotEmpty($this->node->get('element4')); 296 297 // Remove more elements. 298 $this->assertTrue($this->node->get('remove2')->remove()); 299 $this->assertFalse($this->node->get('remove2')); 300 301 // Make sure that we can add element after removal. 302 $this->node->add('extra element 5', null, navigation_node::TYPE_CUSTOM, null, 'element5'); 303 $this->assertNotEmpty($this->node->get('element5')); 304 305 $this->assertTrue($remove2->get('remove3')->remove()); 306 307 $this->assertFalse($this->node->get('remove1')); 308 $this->assertFalse($this->node->get('remove2')); 309 } 310 311 public function test_node_remove_class() { 312 $this->setup_node(); 313 314 $this->node->add_class('testclass'); 315 $this->assertTrue($this->node->remove_class('testclass')); 316 $this->assertNotContains('testclass', $this->node->classes); 317 } 318 319 public function test_module_extends_navigation() { 320 $node = new exposed_global_navigation(); 321 // Create an initial tree structure to work with. 322 $cat1 = $node->add('category 1', null, navigation_node::TYPE_CATEGORY, null, 'cat1'); 323 $cat2 = $node->add('category 2', null, navigation_node::TYPE_CATEGORY, null, 'cat2'); 324 $cat3 = $node->add('category 3', null, navigation_node::TYPE_CATEGORY, null, 'cat3'); 325 $sub1 = $cat2->add('sub category 1', null, navigation_node::TYPE_CATEGORY, null, 'sub1'); 326 $sub2 = $cat2->add('sub category 2', null, navigation_node::TYPE_CATEGORY, null, 'sub2'); 327 $sub3 = $cat2->add('sub category 3', null, navigation_node::TYPE_CATEGORY, null, 'sub3'); 328 $course1 = $sub2->add('course 1', null, navigation_node::TYPE_COURSE, null, 'course1'); 329 $course2 = $sub2->add('course 2', null, navigation_node::TYPE_COURSE, null, 'course2'); 330 $course3 = $sub2->add('course 3', null, navigation_node::TYPE_COURSE, null, 'course3'); 331 $section1 = $course2->add('section 1', null, navigation_node::TYPE_SECTION, null, 'sec1'); 332 $section2 = $course2->add('section 2', null, navigation_node::TYPE_SECTION, null, 'sec2'); 333 $section3 = $course2->add('section 3', null, navigation_node::TYPE_SECTION, null, 'sec3'); 334 $act1 = $section2->add('activity 1', null, navigation_node::TYPE_ACTIVITY, null, 'act1'); 335 $act2 = $section2->add('activity 2', null, navigation_node::TYPE_ACTIVITY, null, 'act2'); 336 $act3 = $section2->add('activity 3', null, navigation_node::TYPE_ACTIVITY, null, 'act3'); 337 $res1 = $section2->add('resource 1', null, navigation_node::TYPE_RESOURCE, null, 'res1'); 338 $res2 = $section2->add('resource 2', null, navigation_node::TYPE_RESOURCE, null, 'res2'); 339 $res3 = $section2->add('resource 3', null, navigation_node::TYPE_RESOURCE, null, 'res3'); 340 341 $this->assertTrue($node->exposed_module_extends_navigation('data')); 342 $this->assertFalse($node->exposed_module_extends_navigation('test1')); 343 } 344 345 public function test_navbar_prepend_and_add() { 346 global $PAGE; 347 // Unfortunate hack needed because people use global $PAGE around the place. 348 $PAGE->set_url('/'); 349 350 // We need to reset after this test because we using the generator. 351 $this->resetAfterTest(); 352 353 $generator = self::getDataGenerator(); 354 $cat1 = $generator->create_category(); 355 $cat2 = $generator->create_category(array('parent' => $cat1->id)); 356 $course = $generator->create_course(array('category' => $cat2->id)); 357 358 $page = new \moodle_page(); 359 $page->set_course($course); 360 $page->set_url(new \moodle_url('/course/view.php', array('id' => $course->id))); 361 $page->navbar->prepend('test 1'); 362 $page->navbar->prepend('test 2'); 363 $page->navbar->add('test 3'); 364 $page->navbar->add('test 4'); 365 366 $items = $page->navbar->get_items(); 367 foreach ($items as $item) { 368 $this->assertInstanceOf('navigation_node', $item); 369 } 370 371 $i = 0; 372 $this->assertSame('test 1', $items[$i++]->text); 373 $this->assertSame('test 2', $items[$i++]->text); 374 $this->assertSame('home', $items[$i++]->key); 375 $this->assertSame('courses', $items[$i++]->key); 376 $this->assertSame($cat1->id, $items[$i++]->key); 377 $this->assertSame($cat2->id, $items[$i++]->key); 378 $this->assertSame($course->id, $items[$i++]->key); 379 $this->assertSame('test 3', $items[$i++]->text); 380 $this->assertSame('test 4', $items[$i++]->text); 381 382 return $page; 383 } 384 385 /** 386 * @depends test_navbar_prepend_and_add 387 * @param $node 388 */ 389 public function test_navbar_has_items(\moodle_page $page) { 390 $this->resetAfterTest(); 391 392 $this->assertTrue($page->navbar->has_items()); 393 } 394 395 public function test_cache__get() { 396 $cache = new navigation_cache('unittest_nav'); 397 $cache->anysetvariable = true; 398 399 $this->assertTrue($cache->anysetvariable); 400 $this->assertEquals($cache->notasetvariable, null); 401 } 402 403 public function test_cache__set() { 404 $cache = new navigation_cache('unittest_nav'); 405 $cache->anysetvariable = true; 406 407 $cache->myname = 'Sam Hemelryk'; 408 $this->assertTrue($cache->cached('myname')); 409 $this->assertSame('Sam Hemelryk', $cache->myname); 410 } 411 412 public function test_cache_cached() { 413 $cache = new navigation_cache('unittest_nav'); 414 $cache->anysetvariable = true; 415 416 $this->assertTrue($cache->cached('anysetvariable')); 417 $this->assertFalse($cache->cached('notasetvariable')); 418 } 419 420 public function test_cache_clear() { 421 $cache = new navigation_cache('unittest_nav'); 422 $cache->anysetvariable = true; 423 424 $cache = clone($cache); 425 $this->assertTrue($cache->cached('anysetvariable')); 426 $cache->clear(); 427 $this->assertFalse($cache->cached('anysetvariable')); 428 } 429 430 public function test_cache_set() { 431 $cache = new navigation_cache('unittest_nav'); 432 $cache->anysetvariable = true; 433 434 $cache->set('software', 'Moodle'); 435 $this->assertTrue($cache->cached('software')); 436 $this->assertEquals($cache->software, 'Moodle'); 437 } 438 439 public function test_setting___construct() { 440 global $PAGE, $SITE; 441 442 $this->resetAfterTest(false); 443 444 $PAGE->set_url('/'); 445 $PAGE->set_course($SITE); 446 447 $node = new exposed_settings_navigation(); 448 449 return $node; 450 } 451 452 /** 453 * @depends test_setting___construct 454 * @param mixed $node 455 * @return mixed 456 */ 457 public function test_setting__initialise($node) { 458 $this->resetAfterTest(false); 459 460 $node->initialise(); 461 $this->assertEquals($node->id, 'settingsnav'); 462 463 return $node; 464 } 465 466 /** 467 * Test that users with the correct permissions can view the preferences page. 468 */ 469 public function test_can_view_user_preferences() { 470 global $PAGE, $DB, $SITE; 471 $this->resetAfterTest(); 472 473 $persontoview = $this->getDataGenerator()->create_user(); 474 $persondoingtheviewing = $this->getDataGenerator()->create_user(); 475 476 $PAGE->set_url('/'); 477 $PAGE->set_course($SITE); 478 479 // Check that a standard user can not view the preferences page. 480 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 481 $this->getDataGenerator()->role_assign($studentrole->id, $persondoingtheviewing->id); 482 $this->setUser($persondoingtheviewing); 483 $settingsnav = new exposed_settings_navigation(); 484 $settingsnav->initialise(); 485 $settingsnav->extend_for_user($persontoview->id); 486 $this->assertFalse($settingsnav->can_view_user_preferences($persontoview->id)); 487 488 // Set persondoingtheviewing as a manager. 489 $managerrole = $DB->get_record('role', array('shortname' => 'manager')); 490 $this->getDataGenerator()->role_assign($managerrole->id, $persondoingtheviewing->id); 491 $settingsnav = new exposed_settings_navigation(); 492 $settingsnav->initialise(); 493 $settingsnav->extend_for_user($persontoview->id); 494 $this->assertTrue($settingsnav->can_view_user_preferences($persontoview->id)); 495 496 // Check that the admin can view the preferences page. 497 $this->setAdminUser(); 498 $settingsnav = new exposed_settings_navigation(); 499 $settingsnav->initialise(); 500 $settingsnav->extend_for_user($persontoview->id); 501 $preferencenode = $settingsnav->find('userviewingsettings' . $persontoview->id, null); 502 $this->assertTrue($settingsnav->can_view_user_preferences($persontoview->id)); 503 } 504 505 /** 506 * @depends test_setting__initialise 507 * @param mixed $node 508 * @return mixed 509 */ 510 public function test_setting_in_alternative_role($node) { 511 $this->resetAfterTest(); 512 513 $this->assertFalse($node->exposed_in_alternative_role()); 514 } 515 516 517 public function test_navigation_node_collection_remove_with_no_type() { 518 $navigationnodecollection = new navigation_node_collection(); 519 $this->setup_node(); 520 $this->node->key = 100; 521 522 // Test it's empty 523 $this->assertEquals(0, count($navigationnodecollection->get_key_list())); 524 525 // Add a node 526 $navigationnodecollection->add($this->node); 527 528 // Test it's not empty 529 $this->assertEquals(1, count($navigationnodecollection->get_key_list())); 530 531 // Remove a node - passing key only! 532 $this->assertTrue($navigationnodecollection->remove(100)); 533 534 // Test it's empty again! 535 $this->assertEquals(0, count($navigationnodecollection->get_key_list())); 536 } 537 538 public function test_navigation_node_collection_remove_with_type() { 539 $navigationnodecollection = new navigation_node_collection(); 540 $this->setup_node(); 541 $this->node->key = 100; 542 543 // Test it's empty 544 $this->assertEquals(0, count($navigationnodecollection->get_key_list())); 545 546 // Add a node 547 $navigationnodecollection->add($this->node); 548 549 // Test it's not empty 550 $this->assertEquals(1, count($navigationnodecollection->get_key_list())); 551 552 // Remove a node - passing type 553 $this->assertTrue($navigationnodecollection->remove(100, 1)); 554 555 // Test it's empty again! 556 $this->assertEquals(0, count($navigationnodecollection->get_key_list())); 557 } 558 559 /** 560 * Test the set_force_into_more_menu method. 561 * 562 * @param bool $haschildren Whether the navigation node has children nodes 563 * @param bool $forceintomoremenu Whether to force the navigation node and its children into the "more" menu 564 * @dataProvider set_force_into_more_menu_provider 565 */ 566 public function test_set_force_into_more_menu(bool $haschildren, bool $forceintomoremenu) { 567 // Create a navigation node. 568 $node = new navigation_node(['text' => 'Navigation node', 'key' => 'navnode']); 569 570 // If required, add some children nodes to the navigation node. 571 if ($haschildren) { 572 for ($i = 1; $i <= 3; $i++) { 573 $node->add("Child navigation node {$i}"); 574 } 575 } 576 577 $node->set_force_into_more_menu($forceintomoremenu); 578 // Assert that the expected value has been assigned to the 'forceintomoremenu' property 579 // in the navigation node and its children. 580 $this->assertEquals($forceintomoremenu, $node->forceintomoremenu); 581 foreach ($node->children as $child) { 582 $this->assertEquals($forceintomoremenu, $child->forceintomoremenu); 583 } 584 } 585 586 /** 587 * Data provider for the test_set_force_into_more_menu function. 588 * 589 * @return array 590 */ 591 public function set_force_into_more_menu_provider(): array { 592 return [ 593 'Navigation node without any children nodes; Force into "more" menu => true.' => 594 [ 595 false, 596 true, 597 ], 598 'Navigation node with children nodes; Force into "more" menu => true.' => 599 [ 600 true, 601 true, 602 ], 603 'Navigation node with children nodes; Force into "more" menu => false.' => 604 [ 605 true, 606 false, 607 ], 608 ]; 609 } 610 611 /** 612 * Test the is_action_link method. 613 * 614 * @param navigation_node $node The sample navigation node 615 * @param bool $expected Whether the navigation node contains an action link 616 * @dataProvider is_action_link_provider 617 * @covers navigation_node::is_action_link 618 */ 619 public function test_is_action_link(navigation_node $node, bool $expected) { 620 $this->assertEquals($node->is_action_link(), $expected); 621 } 622 623 /** 624 * Data provider for the test_is_action_link function. 625 * 626 * @return array 627 */ 628 public function is_action_link_provider(): array { 629 return [ 630 'The navigation node has an action link.' => 631 [ 632 navigation_node::create('Node', new action_link(new \moodle_url('/'), '', 633 new popup_action('click', new \moodle_url('/'))), navigation_node::TYPE_SETTING), 634 true 635 ], 636 637 'The navigation node does not have an action link.' => 638 [ 639 navigation_node::create('Node', new \moodle_url('/'), navigation_node::TYPE_SETTING), 640 false 641 ], 642 ]; 643 } 644 645 /** 646 * Test the action_link_actions method. 647 * 648 * @param navigation_node $node The sample navigation node 649 * @dataProvider action_link_actions_provider 650 * @covers navigation_node::action_link_actions 651 */ 652 public function test_action_link_actions(navigation_node $node) { 653 // Get the formatted array of action link actions. 654 $data = $node->action_link_actions(); 655 // The navigation node has an action link. 656 if ($node->action instanceof action_link) { 657 if (!empty($node->action->actions)) { // There are actions added to the action link. 658 $this->assertArrayHasKey('actions', $data); 659 $this->assertCount(1, $data['actions']); 660 $expected = (object)[ 661 'id' => $node->action->attributes['id'], 662 'event' => $node->action->actions[0]->event, 663 'jsfunction' => $node->action->actions[0]->jsfunction, 664 'jsfunctionargs' => json_encode($node->action->actions[0]->jsfunctionargs) 665 ]; 666 $this->assertEquals($expected, $data['actions'][0]); 667 } else { // There are no actions added to the action link. 668 $this->assertArrayHasKey('actions', $data); 669 $this->assertEmpty($data['actions']); 670 } 671 } else { // The navigation node does not have an action link. 672 $this->assertEmpty($data); 673 } 674 } 675 676 /** 677 * Data provider for the test_action_link_actions function. 678 * 679 * @return array 680 */ 681 public function action_link_actions_provider(): array { 682 return [ 683 'The navigation node has an action link with an action attached.' => 684 [ 685 navigation_node::create('Node', new action_link(new \moodle_url('/'), '', 686 new popup_action('click', new \moodle_url('/'))), navigation_node::TYPE_SETTING), 687 ], 688 'The navigation node has an action link without an action.' => 689 [ 690 navigation_node::create('Node', new action_link(new \moodle_url('/'), '', null), 691 navigation_node::TYPE_SETTING), 692 ], 693 'The navigation node does not have an action link.' => 694 [ 695 navigation_node::create('Node', new \moodle_url('/'), navigation_node::TYPE_SETTING), 696 ], 697 ]; 698 } 699 } 700 701 702 /** 703 * This is a dummy object that allows us to call protected methods within the 704 * global navigation class by prefixing the methods with `exposed_` 705 */ 706 class exposed_global_navigation extends global_navigation { 707 protected $exposedkey = 'exposed_'; 708 public function __construct(\moodle_page $page=null) { 709 global $PAGE; 710 if ($page === null) { 711 $page = $PAGE; 712 } 713 parent::__construct($page); 714 $this->cache = new navigation_cache('unittest_nav'); 715 } 716 public function __call($method, $arguments) { 717 if (strpos($method, $this->exposedkey) !== false) { 718 $method = substr($method, strlen($this->exposedkey)); 719 } 720 if (method_exists($this, $method)) { 721 return call_user_func_array(array($this, $method), $arguments); 722 } 723 throw new \coding_exception('You have attempted to access a method that does not exist for the given object '.$method, DEBUG_DEVELOPER); 724 } 725 public function set_initialised() { 726 $this->initialised = true; 727 } 728 } 729 730 731 class mock_initialise_global_navigation extends global_navigation { 732 733 protected static $count = 1; 734 735 public function load_for_category() { 736 $this->add('load_for_category', null, null, null, 'initcall'.self::$count); 737 self::$count++; 738 return 0; 739 } 740 741 public function load_for_course() { 742 $this->add('load_for_course', null, null, null, 'initcall'.self::$count); 743 self::$count++; 744 return 0; 745 } 746 747 public function load_for_activity() { 748 $this->add('load_for_activity', null, null, null, 'initcall'.self::$count); 749 self::$count++; 750 return 0; 751 } 752 753 public function load_for_user($user=null, $forceforcontext=false) { 754 $this->add('load_for_user', null, null, null, 'initcall'.self::$count); 755 self::$count++; 756 return 0; 757 } 758 } 759 760 /** 761 * This is a dummy object that allows us to call protected methods within the 762 * global navigation class by prefixing the methods with `exposed_`. 763 */ 764 class exposed_navbar extends navbar { 765 protected $exposedkey = 'exposed_'; 766 public function __construct(\moodle_page $page) { 767 parent::__construct($page); 768 $this->cache = new navigation_cache('unittest_nav'); 769 } 770 public function __call($method, $arguments) { 771 if (strpos($method, $this->exposedkey) !== false) { 772 $method = substr($method, strlen($this->exposedkey)); 773 } 774 if (method_exists($this, $method)) { 775 return call_user_func_array(array($this, $method), $arguments); 776 } 777 throw new \coding_exception('You have attempted to access a method that does not exist for the given object '.$method, DEBUG_DEVELOPER); 778 } 779 } 780 781 class navigation_exposed_moodle_page extends \moodle_page { 782 public function set_navigation(navigation_node $node) { 783 $this->_navigation = $node; 784 } 785 } 786 787 /** 788 * This is a dummy object that allows us to call protected methods within the 789 * global navigation class by prefixing the methods with `exposed_`. 790 */ 791 class exposed_settings_navigation extends settings_navigation { 792 protected $exposedkey = 'exposed_'; 793 public function __construct() { 794 global $PAGE; 795 parent::__construct($PAGE); 796 $this->cache = new navigation_cache('unittest_nav'); 797 } 798 public function __call($method, $arguments) { 799 if (strpos($method, $this->exposedkey) !== false) { 800 $method = substr($method, strlen($this->exposedkey)); 801 } 802 if (method_exists($this, $method)) { 803 return call_user_func_array(array($this, $method), $arguments); 804 } 805 throw new \coding_exception('You have attempted to access a method that does not exist for the given object '.$method, DEBUG_DEVELOPER); 806 } 807 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body