Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   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 theme_boost;
  18  
  19  /**
  20   * Test the boostnavbar file
  21   *
  22   * @package    theme_boost
  23   * @covers     \theme_boost\boostnavbar
  24   * @copyright  2021 Peter Dias
  25   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  26   */
  27  class boostnavbar_test extends \advanced_testcase {
  28      /**
  29       * Provider for test_remove_no_link_items
  30       * The setup and expected arrays are defined as an array of 'nodekey' => $hasaction
  31       *
  32       * @return array
  33       */
  34      public function remove_no_link_items_provider(): array {
  35          return [
  36              'All nodes have links links including leaf node. Set to remove section nodes.' => [
  37                  [
  38                      'node1' => ['hasaction' => true, 'issection' => false],
  39                      'node2' => ['hasaction' => true, 'issection' => false],
  40                      'node3' => ['hasaction' => true, 'issection' => false],
  41                  ],
  42                  true,
  43                  [
  44                      'Home' => true,
  45                      'Courses' => true,
  46                      'tc_1' => true,
  47                      'node1' => true,
  48                      'node2' => true,
  49                      'node3' => true,
  50                  ]
  51              ],
  52              'Only some parent nodes have links. Leaf node has a link. Set to remove section nodes.' => [
  53                  [
  54                      'node1' => ['hasaction' => false, 'issection' => false],
  55                      'node2' => ['hasaction' => true, 'issection' => false],
  56                      'node3' => ['hasaction' => true, 'issection' => false],
  57                  ],
  58                  true,
  59                  [
  60                      'Home' => true,
  61                      'Courses' => true,
  62                      'tc_1' => true,
  63                      'node2' => true,
  64                      'node3' => true,
  65                  ]
  66              ],
  67              'All parent nodes do not have links. Leaf node has a link. Set to remove section nodes.' => [
  68                  [
  69                      'node1' => ['hasaction' => false, 'issection' => false],
  70                      'node2' => ['hasaction' => false, 'issection' => false],
  71                      'node3' => ['hasaction' => true, 'issection' => false],
  72                  ],
  73                  true,
  74                  [
  75                      'Home' => true,
  76                      'Courses' => true,
  77                      'tc_1' => true,
  78                      'node3' => true,
  79                  ]
  80              ],
  81              'All parent nodes have links. Leaf node does not has a link. Set to remove section nodes.' => [
  82                  [
  83                      'node1' => ['hasaction' => true, 'issection' => false],
  84                      'node2' => ['hasaction' => true, 'issection' => false],
  85                      'node3' => ['hasaction' => false, 'issection' => false],
  86                  ],
  87                  true,
  88                  [
  89                      'Home' => true,
  90                      'Courses' => true,
  91                      'tc_1' => true,
  92                      'node1' => true,
  93                      'node2' => true,
  94                      'node3' => false,
  95                  ]
  96              ],
  97              'All parent nodes do not have links. Leaf node does not has a link. Set to remove section nodes.' => [
  98                  [
  99                      'node1' => ['hasaction' => false, 'issection' => false],
 100                      'node2' => ['hasaction' => false, 'issection' => false],
 101                      'node3' => ['hasaction' => false, 'issection' => false],
 102                  ],
 103                  true,
 104                  [
 105                      'Home' => true,
 106                      'Courses' => true,
 107                      'tc_1' => true,
 108                      'node3' => false,
 109                  ]
 110              ],
 111              'Some parent nodes do not have links. Leaf node does not has a link. Set to remove section nodes.' => [
 112                  [
 113                      'node1' => ['hasaction' => true, 'issection' => false],
 114                      'node2' => ['hasaction' => false, 'issection' => false],
 115                      'node3' => ['hasaction' => false, 'issection' => false],
 116                  ],
 117                  true,
 118                  [
 119                      'Home' => true,
 120                      'Courses' => true,
 121                      'tc_1' => true,
 122                      'node1' => true,
 123                      'node3' => false,
 124                  ]
 125              ],
 126              'All nodes have links links including leaf node and section nodes. Set to remove section nodes.' => [
 127                  [
 128                      'node1' => ['hasaction' => true, 'issection' => false],
 129                      'node2' => ['hasaction' => true, 'issection' => false],
 130                      'sectionnode1' => ['hasaction' => true, 'issection' => true],
 131                      'node3' => ['hasaction' => true, 'issection' => false],
 132                  ],
 133                  true,
 134                  [
 135                      'Home' => true,
 136                      'Courses' => true,
 137                      'tc_1' => true,
 138                      'node1' => true,
 139                      'node2' => true,
 140                      'node3' => true,
 141                  ]
 142              ],
 143              'All nodes have links links including leaf node and section nodes. Set to not remove section nodes.' => [
 144                  [
 145                      'node1' => ['hasaction' => true, 'issection' => false],
 146                      'node2' => ['hasaction' => true, 'issection' => false],
 147                      'sectionnode1' => ['hasaction' => true, 'issection' => true],
 148                      'node3' => ['hasaction' => true, 'issection' => false],
 149                  ],
 150                  false,
 151                  [
 152                      'Home' => true,
 153                      'Courses' => true,
 154                      'tc_1' => true,
 155                      'node1' => true,
 156                      'node2' => true,
 157                      'sectionnode1' => true,
 158                      'node3' => true,
 159                  ]
 160              ],
 161              'Only some parent nodes have links. Section node does not have a link. Set to not remove section nodes.' => [
 162                  [
 163                      'node1' => ['hasaction' => false, 'issection' => false],
 164                      'node2' => ['hasaction' => true, 'issection' => false],
 165                      'sectionnode1' => ['hasaction' => false, 'issection' => true],
 166                      'node3' => ['hasaction' => true, 'issection' => false],
 167                  ],
 168                  true,
 169                  [
 170                      'Home' => true,
 171                      'Courses' => true,
 172                      'tc_1' => true,
 173                      'node2' => true,
 174                      'node3' => true,
 175                  ]
 176              ]
 177          ];
 178      }
 179      /**
 180       * Test the remove_no_link_items function
 181       *
 182       * @dataProvider remove_no_link_items_provider
 183       * @param array $setup
 184       * @param bool $removesectionnodes Whether to remove the section nodes with an associated action.
 185       * @param array $expected
 186       * @throws \ReflectionException
 187       */
 188      public function test_remove_no_link_items(array $setup, bool $removesectionnodes, array $expected) {
 189          global $PAGE;
 190  
 191          $this->resetAfterTest();
 192          // Unfortunate hack needed because people use global $PAGE around the place.
 193          $PAGE->set_url('/');
 194          $course = $this->getDataGenerator()->create_course();
 195          $page = new \moodle_page();
 196          $page->set_course($course);
 197          $page->set_url(new \moodle_url('/course/view.php', array('id' => $course->id)));
 198          // A dummy url to use. We don't care where it's pointing to.
 199          $url = new \moodle_url('/');
 200          foreach ($setup as $key => $value) {
 201              $page->navbar->add($key, $value['hasaction'] ? $url : null,
 202                  $value['issection'] ? \navigation_node::TYPE_SECTION : null);
 203          }
 204  
 205          $boostnavbar = $this->getMockBuilder(boostnavbar::class)
 206              ->disableOriginalConstructor()
 207              ->onlyMethods([])
 208              ->getMock();
 209  
 210          $rc = new \ReflectionClass(boostnavbar::class);
 211          $rcp = $rc->getProperty('items');
 212          $rcp->setAccessible(true);
 213          $rcp->setValue($boostnavbar, $page->navbar->get_items());
 214  
 215          // Make the call to the function.
 216          $rcm = $rc->getMethod('remove_no_link_items');
 217          $rcm->setAccessible(true);
 218          $rcm->invoke($boostnavbar, $removesectionnodes);
 219  
 220          // Get the value for the class variable that the function modifies.
 221          $values = $rcp->getValue($boostnavbar);
 222          $actual = [];
 223          foreach ($values as $value) {
 224              $actual[$value->text] = $value->has_action();
 225          }
 226          $this->assertEquals($expected, $actual);
 227      }
 228  
 229      /**
 230       * Provider for test_remove_duplicate_items.
 231       *
 232       * @return array
 233       */
 234      public function remove_duplicate_items_provider(): array {
 235          global $CFG;
 236  
 237          return [
 238              'Breadcrumb items with identical text and action url (actions of same type moodle_url).' => [
 239                  [
 240                      [
 241                          'text' => 'Node 1',
 242                          'action' => new \moodle_url('/page1.php')
 243                      ],
 244                      [
 245                          'text' => 'Node 2',
 246                          'action' => new \moodle_url('/page2.php', ['id' => 1])
 247                      ],
 248                      [
 249                          'text' => 'Node 4',
 250                          'action' => new \moodle_url('/page4.php', ['id' => 1])
 251                      ],
 252                      [
 253                          'text' => 'Node 2',
 254                          'action' => new \moodle_url('/page2.php', ['id' => 1])
 255                      ],
 256                  ],
 257                  ['Home', 'Node 1', 'Node 4', 'Node 2']
 258              ],
 259              'Breadcrumb items with identical text and action url (actions of different type moodle_url/text).' => [
 260                  [
 261                      [
 262                          'text' => 'Node 1',
 263                          'action' => new \moodle_url('/page1.php')
 264                      ],
 265                      [
 266                          'text' => 'Node 2',
 267                          'action' => new \moodle_url('/page2.php', ['id' => 1])
 268                      ],
 269                      [
 270                          'text' => 'Node 4',
 271                          'action' => new \moodle_url('/page4.php', ['id' => 1])
 272                      ],
 273                      [
 274                          'text' => 'Node 2',
 275                          'action' => "{$CFG->wwwroot}/page2.php?id=1"
 276                      ],
 277                  ],
 278                  ['Home', 'Node 1', 'Node 4', 'Node 2']
 279              ],
 280              'Breadcrumb items with identical text and action url (actions of different type moodle_url/action_link).' => [
 281                  [
 282                      [
 283                          'text' => 'Node 1',
 284                          'action' => new \moodle_url('/page1.php')
 285                      ],
 286                      [
 287                          'text' => 'Node 2',
 288                          'action' => new \moodle_url('/page2.php', ['id' => 1])
 289                      ],
 290                      [
 291                          'text' => 'Node 4',
 292                          'action' => new \moodle_url('/page4.php', ['id' => 1])
 293                      ],
 294                      [
 295                          'text' => 'Node 2',
 296                          'action' => new \action_link(new \moodle_url('/page2.php', ['id' => 1]), 'Action link')
 297                      ],
 298                  ],
 299                  ['Home', 'Node 1', 'Node 4', 'Node 2']
 300              ],
 301              'Breadcrumbs items with identical text but not identical action url.' => [
 302                  [
 303                      [
 304                          'text' => 'Node 1',
 305                          'action' => new \moodle_url('/page1.php')
 306                      ],
 307                      [
 308                          'text' => 'Node 2',
 309                          'action' => new \moodle_url('/page2.php', ['id' => 1])
 310                      ],
 311                      [
 312                          'text' => 'Node 2',
 313                          'action' => new \moodle_url('/page2.php', ['id' => 2])
 314                      ],
 315                      [
 316                          'text' => 'Node 4',
 317                          'action' => new \moodle_url('/page4.php', ['id' => 1])
 318                      ],
 319                  ],
 320                  ['Home', 'Node 1', 'Node 2', 'Node 2', 'Node 4']
 321              ],
 322              'Breadcrumb items with identical action url but not identical text.' => [
 323                  [
 324                      [
 325                          'text' => 'Node 1',
 326                          'action' => new \moodle_url('/page1.php')
 327                      ],
 328                      [
 329                          'text' => 'Node 2',
 330                          'action' => new \moodle_url('/page2.php', ['id' => 1])
 331                      ],
 332                      [
 333                          'text' => 'Node 3',
 334                          'action' => new \moodle_url('/page2.php', ['id' => 1])
 335                      ],
 336                      [
 337                          'text' => 'Node 4',
 338                          'action' => new \moodle_url('/page4.php', ['id' => 1])
 339                      ],
 340                  ],
 341                  ['Home', 'Node 1', 'Node 2', 'Node 3', 'Node 4']
 342              ],
 343              'Breadcrumb items without any identical action url or text.' => [
 344                  [
 345                      [
 346                          'text' => 'Node 1',
 347                          'action' => new \moodle_url('/page1.php')
 348                      ],
 349                      [
 350                          'text' => 'Node 2',
 351                          'action' => new \moodle_url('/page2.php', ['id' => 1])
 352                      ],
 353                      [
 354                          'text' => 'Node 3',
 355                          'action' => new \moodle_url('/page3.php', ['id' => 1])
 356                      ],
 357                      [
 358                          'text' => 'Node 4',
 359                          'action' => new \moodle_url('/page4.php', ['id' => 1])
 360                      ],
 361                  ],
 362                  ['Home', 'Node 1', 'Node 2', 'Node 3', 'Node 4']
 363              ],
 364          ];
 365      }
 366  
 367      /**
 368       * Test the remove_duplicate_items function.
 369       *
 370       * @dataProvider remove_duplicate_items_provider
 371       * @param array $navbarnodes The array containing the text and action of the nodes to be added to the navbar
 372       * @param array $expected The array containing the text of the expected navbar nodes
 373       */
 374      public function test_remove_duplicate_items(array $navbarnodes, array $expected) {
 375          $this->resetAfterTest();
 376          $page = new \moodle_page();
 377          $page->set_url('/');
 378  
 379          // Add the navbar nodes.
 380          foreach ($navbarnodes as $node) {
 381              $page->navbar->add($node['text'], $node['action'], \navigation_node::TYPE_CUSTOM);
 382          }
 383  
 384          $boostnavbar = $this->getMockBuilder(boostnavbar::class)
 385              ->disableOriginalConstructor()
 386              ->onlyMethods([])
 387              ->getMock();
 388  
 389          $rc = new \ReflectionClass(boostnavbar::class);
 390          $rcp = $rc->getProperty('items');
 391          $rcp->setAccessible(true);
 392          $rcp->setValue($boostnavbar, $page->navbar->get_items());
 393  
 394          // Make the call to the function.
 395          $rcm = $rc->getMethod('remove_duplicate_items');
 396          $rcm->setAccessible(true);
 397          $rcm->invoke($boostnavbar);
 398  
 399          // Get the value for the class variable that the function modifies.
 400          $values = $rcp->getValue($boostnavbar);
 401          $actual = [];
 402          foreach ($values as $value) {
 403              $actual[] = $value->text;
 404          }
 405          $this->assertEquals($expected, $actual);
 406      }
 407  
 408  
 409      /**
 410       * Provider for test_remove_items_that_exist_in_navigation.
 411       *
 412       * @return array
 413       */
 414      public function remove_items_that_exist_in_navigation_provider(): array {
 415          global $CFG;
 416  
 417          return [
 418              'Item with identical action url and text exists in the primary navigation menu.' => [
 419                  'primary',
 420                  [
 421                      [
 422                          'text' => 'Node 1',
 423                          'action' => new \moodle_url('/page1.php')
 424                      ],
 425                  ],
 426                  [
 427                      'Node 1' => new \moodle_url('/page1.php'),
 428                      'Node 2' => new \moodle_url('/page2.php'),
 429                      'Node 3' => new \moodle_url('/page1.php'),
 430                  ],
 431                  ['Node 2', 'Node 3']
 432              ],
 433              'Item with identical action url and text exists in the secondary navigation menu.' => [
 434                  'secondary',
 435                  [
 436                      [
 437                          'text' => 'Node 2',
 438                          'action' => new \moodle_url('/page2.php')
 439                      ],
 440                  ],
 441                  [
 442                      'Node 1' => new \moodle_url('/page1.php'),
 443                      'Node 2' => new \moodle_url('/page2.php'),
 444                      'Node 3' => new \moodle_url('/page1.php'),
 445                  ],
 446                  ['Home', 'Node 1', 'Node 3']
 447              ],
 448              'Multiple items with identical action url and text exist in the secondary navigation menu.' => [
 449                  'secondary',
 450                  [
 451                      [
 452                          'text' => 'Node 2',
 453                          'action' => new \moodle_url('/page2.php')
 454                      ],
 455                      [
 456                          'text' => 'Node 3',
 457                          'action' => new \moodle_url('/page3.php')
 458                      ],
 459                  ],
 460                  [
 461                      'Node 1' => new \moodle_url('/page1.php'),
 462                      'Node 2' => "{$CFG->wwwroot}/page2.php",
 463                      'Node 3' => new \action_link(new \moodle_url('/page3.php'), 'Action link'),
 464                  ],
 465                  ['Home', 'Node 1']
 466              ],
 467              'No items with identical action url and text in the secondary navigation menu.' => [
 468                  'secondary',
 469                  [
 470                      [
 471                          'text' => 'Node 4',
 472                          'action' => new \moodle_url('/page4.php')
 473                      ],
 474                  ],
 475                  [
 476                      'Node 1' => new \moodle_url('/page1.php'),
 477                      'Node 2' => new \moodle_url('/page2.php'),
 478                      'Node 3' => new \moodle_url('/page1.php'),
 479                  ],
 480                  ['Home', 'Node 1', 'Node 2', 'Node 3']
 481              ],
 482          ];
 483      }
 484  
 485      /**
 486       * Test the remove_items_that_exist_in_navigation function.
 487       *
 488       * @dataProvider remove_items_that_exist_in_navigation_provider
 489       * @param string $navmenu The name of the navigation menu we would like to use (primary or secondary)
 490       * @param array $navmenunodes The array containing the text and action of the nodes to be added to the navigation menu
 491       * @param array $navbarnodes Array containing the text => action of the nodes to be added to the navbar
 492       * @param array $expected Array containing the text of the expected navbar nodes after the filtering
 493       */
 494      public function test_remove_items_that_exist_in_navigation(string $navmenu, array $navmenunodes, array $navbarnodes,
 495              array $expected) {
 496          global $PAGE;
 497  
 498          // Unfortunate hack needed because people use global $PAGE around the place.
 499          $PAGE->set_url('/');
 500          $this->resetAfterTest();
 501          $page = new \moodle_page();
 502          $page->set_url('/');
 503  
 504          switch ($navmenu) {
 505              case 'primary':
 506                  $navigationmenu = new \core\navigation\views\primary($page);
 507                  break;
 508              case 'secondary':
 509                  $navigationmenu = new \core\navigation\views\secondary($page);
 510          }
 511  
 512          $navigationmenu->initialise();
 513          // Add the additional nodes to the navigation menu.
 514          foreach ($navmenunodes as $navmenunode) {
 515              $navigationmenu->add($navmenunode['text'], $navmenunode['action'], \navigation_node::TYPE_CUSTOM);
 516          }
 517  
 518          // Add the additional navbar nodes.
 519          foreach ($navbarnodes as $text => $action) {
 520              $page->navbar->add($text, $action, \navigation_node::TYPE_CUSTOM);
 521          }
 522  
 523          $boostnavbar = $this->getMockBuilder(boostnavbar::class)
 524              ->disableOriginalConstructor()
 525              ->onlyMethods([])
 526              ->getMock();
 527  
 528          $rc = new \ReflectionClass(boostnavbar::class);
 529          $rcp = $rc->getProperty('items');
 530          $rcp->setAccessible(true);
 531          $rcp->setValue($boostnavbar, $page->navbar->get_items());
 532  
 533          // Make the call to the function.
 534          $rcm = $rc->getMethod('remove_items_that_exist_in_navigation');
 535          $rcm->setAccessible(true);
 536          $rcm->invoke($boostnavbar, $navigationmenu);
 537  
 538          // Get the value for the class variable that the function modifies.
 539          $values = $rcp->getValue($boostnavbar);
 540          $actual = [];
 541          foreach ($values as $value) {
 542              $actual[] = $value->text;
 543          }
 544          $this->assertEquals($expected, $actual);
 545      }
 546  }