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.

Differences Between: [Versions 310 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 block_contents;
  20  use custom_menu;
  21  use custom_menu_item;
  22  use paging_bar;
  23  use renderer_base;
  24  use single_select;
  25  use theme_config;
  26  use url_select;
  27  use user_picture;
  28  
  29  defined('MOODLE_INTERNAL') || die();
  30  
  31  global $CFG;
  32  require_once($CFG->libdir . '/outputcomponents.php');
  33  
  34  /**
  35   * Unit tests for lib/outputcomponents.php.
  36   *
  37   * @package   core
  38   * @category  test
  39   * @copyright 2011 David Mudrak <david@moodle.com>
  40   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   */
  42  class outputcomponents_test extends \advanced_testcase {
  43  
  44      /**
  45       * Tests user_picture::fields.
  46       *
  47       * @deprecated since Moodle 3.11 MDL-45242
  48       */
  49      public function test_fields_aliasing() {
  50          $fields = user_picture::fields();
  51          $fields = array_map('trim', explode(',', $fields));
  52          $this->assertTrue(in_array('id', $fields));
  53  
  54          $aliased = array();
  55          foreach ($fields as $field) {
  56              if ($field === 'id') {
  57                  $aliased['id'] = 'aliasedid';
  58              } else {
  59                  $aliased[$field] = 'prefix'.$field;
  60              }
  61          }
  62  
  63          $returned = user_picture::fields('', array('custom1', 'id'), 'aliasedid', 'prefix');
  64          $returned = array_map('trim', explode(',', $returned));
  65          $this->assertEquals(count($returned), count($fields) + 1); // Only one extra field added.
  66  
  67          foreach ($fields as $field) {
  68              if ($field === 'id') {
  69                  $expected = "id AS aliasedid";
  70              } else {
  71                  $expected = "$field AS prefix$field";
  72              }
  73              $this->assertContains($expected, $returned, "Expected pattern '$expected' not returned");
  74          }
  75          $this->assertContains("custom1 AS prefixcustom1", $returned, "Expected pattern 'custom1 AS prefixcustom1' not returned");
  76  
  77          // Deprecation warnings for user_picture::fields.
  78          $this->assertDebuggingCalledCount(2);
  79      }
  80  
  81      /**
  82       * Tests user_picture::unalias.
  83       */
  84      public function test_fields_unaliasing() {
  85          $fields = implode(',', \core_user\fields::get_picture_fields());
  86          $fields = array_map('trim', explode(',', $fields));
  87  
  88          $fakerecord = new \stdClass();
  89          $fakerecord->aliasedid = 42;
  90          foreach ($fields as $field) {
  91              if ($field !== 'id') {
  92                  $fakerecord->{'prefix'.$field} = "Value of $field";
  93              }
  94          }
  95          $fakerecord->prefixcustom1 = 'Value of custom1';
  96  
  97          $returned = user_picture::unalias($fakerecord, array('custom1'), 'aliasedid', 'prefix');
  98  
  99          $this->assertEquals(42, $returned->id);
 100          foreach ($fields as $field) {
 101              if ($field !== 'id') {
 102                  $this->assertSame("Value of $field", $returned->{$field});
 103              }
 104          }
 105          $this->assertSame('Value of custom1', $returned->custom1);
 106      }
 107  
 108      /**
 109       * Tests user_picture::unalias with null values.
 110       */
 111      public function test_fields_unaliasing_null() {
 112          $fields = implode(',', \core_user\fields::get_picture_fields());
 113          $fields = array_map('trim', explode(',', $fields));
 114  
 115          $fakerecord = new \stdClass();
 116          $fakerecord->aliasedid = 42;
 117          foreach ($fields as $field) {
 118              if ($field !== 'id') {
 119                  $fakerecord->{'prefix'.$field} = "Value of $field";
 120              }
 121          }
 122          $fakerecord->prefixcustom1 = 'Value of custom1';
 123          $fakerecord->prefiximagealt = null;
 124  
 125          $returned = user_picture::unalias($fakerecord, array('custom1'), 'aliasedid', 'prefix');
 126  
 127          $this->assertEquals(42, $returned->id);
 128          $this->assertNull($returned->imagealt);
 129          foreach ($fields as $field) {
 130              if ($field !== 'id' and $field !== 'imagealt') {
 131                  $this->assertSame("Value of $field", $returned->{$field});
 132              }
 133          }
 134          $this->assertSame('Value of custom1', $returned->custom1);
 135      }
 136  
 137      public function test_get_url() {
 138          global $DB, $CFG, $USER;
 139  
 140          $this->resetAfterTest();
 141  
 142          // Force SVG on so that we have predictable URL's.
 143          $CFG->svgicons = true;
 144  
 145          // Verify new install contains expected defaults.
 146          $this->assertSame(theme_config::DEFAULT_THEME, $CFG->theme);
 147          $this->assertEquals(1, $CFG->slasharguments);
 148          $this->assertEquals(1, $CFG->themerev);
 149          $this->assertEquals(0, $CFG->themedesignermode);
 150          $this->assertSame('https://www.example.com/moodle', $CFG->wwwroot);
 151          $this->assertEquals(0, $CFG->enablegravatar);
 152          $this->assertSame('mm', $CFG->gravatardefaulturl);
 153  
 154          // Create some users.
 155          $page = new \moodle_page();
 156          $page->set_url('/user/profile.php');
 157          $page->set_context(\context_system::instance());
 158          $renderer = $page->get_renderer('core');
 159  
 160          $user1 = $this->getDataGenerator()->create_user(array('picture'=>11, 'email'=>'user1@example.com'));
 161          $context1 = \context_user::instance($user1->id);
 162          $user2 = $this->getDataGenerator()->create_user(array('picture'=>0, 'email'=>'user2@example.com'));
 163          $context2 = \context_user::instance($user2->id);
 164  
 165          // User 3 is deleted.
 166          $user3 = $this->getDataGenerator()->create_user(array('picture'=>1, 'deleted'=>1, 'email'=>'user3@example.com'));
 167          $this->assertNotEmpty(\context_user::instance($user3->id));
 168          $this->assertEquals(0, $user3->picture);
 169          $this->assertNotEquals('user3@example.com', $user3->email);
 170  
 171          // User 4 is incorrectly deleted with its context deleted as well (testing legacy code).
 172          $user4 = $this->getDataGenerator()->create_user(['picture' => 1, 'deleted' => 1, 'email' => 'user4@example.com']);
 173          \context_helper::delete_instance(CONTEXT_USER, $user4->id);
 174          $this->assertEquals(0, $user4->picture);
 175          $this->assertNotEquals('user4@example.com', $user4->email);
 176  
 177          // Try legacy picture == 1.
 178          $user1->picture = 1;
 179          $up1 = new user_picture($user1);
 180          $this->assertSame($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/boost/f2?rev=1', $up1->get_url($page, $renderer)->out(false));
 181          $user1->picture = 11;
 182  
 183          // Try valid user with picture when user context is not cached - 1 query expected.
 184          \context_helper::reset_caches();
 185          $reads = $DB->perf_get_reads();
 186          $up1 = new user_picture($user1);
 187          $this->assertEquals($reads, $DB->perf_get_reads());
 188          $this->assertSame($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/boost/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 189          $this->assertEquals($reads+1, $DB->perf_get_reads());
 190  
 191          // Try valid user with contextid hint - no queries expected.
 192          $user1->contextid = $context1->id;
 193          \context_helper::reset_caches();
 194          $reads = $DB->perf_get_reads();
 195          $up1 = new user_picture($user1);
 196          $this->assertEquals($reads, $DB->perf_get_reads());
 197          $this->assertSame($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/boost/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 198          $this->assertEquals($reads, $DB->perf_get_reads());
 199  
 200          // Try valid user without image - no queries expected.
 201          \context_helper::reset_caches();
 202          $reads = $DB->perf_get_reads();
 203          $up2 = new user_picture($user2);
 204          $this->assertEquals($reads, $DB->perf_get_reads());
 205          $this->assertSame($CFG->wwwroot.'/theme/image.php/boost/core/1/u/f2', $up2->get_url($page, $renderer)->out(false));
 206          $this->assertEquals($reads, $DB->perf_get_reads());
 207  
 208          // Try guessing of deleted users - no queries expected.
 209          unset($user3->deleted);
 210          \context_helper::reset_caches();
 211          $reads = $DB->perf_get_reads();
 212          $up3 = new user_picture($user3);
 213          $this->assertEquals($reads, $DB->perf_get_reads());
 214          $this->assertSame($CFG->wwwroot.'/theme/image.php/boost/core/1/u/f2', $up3->get_url($page, $renderer)->out(false));
 215          $this->assertEquals($reads, $DB->perf_get_reads());
 216  
 217          // Try incorrectly deleted users (with valid email and picture flag, but user context removed) - some DB reads expected.
 218          unset($user4->deleted);
 219          $user4->email = 'user4@example.com';
 220          $user4->picture = 1;
 221          $reads = $DB->perf_get_reads();
 222          $up4 = new user_picture($user4);
 223          $this->assertEquals($reads, $DB->perf_get_reads());
 224          $this->assertSame($CFG->wwwroot.'/theme/image.php/boost/core/1/u/f2', $up4->get_url($page, $renderer)->out(false));
 225          $this->assertGreaterThan($reads, $DB->perf_get_reads());
 226  
 227          // Test gravatar.
 228          set_config('enablegravatar', 1);
 229  
 230          // Deleted user can not have gravatar.
 231          $user3->email = 'deleted';
 232          $user3->picture = 0;
 233          $up3 = new user_picture($user3);
 234          $this->assertSame($CFG->wwwroot.'/theme/image.php/boost/core/1/u/f2', $up3->get_url($page, $renderer)->out(false));
 235          $user4->email = 'deleted';
 236          $user4->picture = 0;
 237          $up4 = new user_picture($user4);
 238          $this->assertSame($CFG->wwwroot.'/theme/image.php/boost/core/1/u/f2', $up4->get_url($page, $renderer)->out(false));
 239  
 240          // Http version.
 241          $CFG->wwwroot = str_replace('https:', 'http:', $CFG->wwwroot);
 242  
 243          // Verify defaults to misteryman (mm).
 244          $up2 = new user_picture($user2);
 245          $this->assertSame('http://www.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?s=35&d=mm', $up2->get_url($page, $renderer)->out(false));
 246  
 247          // Without gravatardefaulturl, verify we pick own file.
 248          set_config('gravatardefaulturl', '');
 249          $up2 = new user_picture($user2);
 250          $this->assertSame('http://www.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?s=35&d=http%3A%2F%2Fwww.example.com%2Fmoodle%2Fpix%2Fu%2Ff2.png', $up2->get_url($page, $renderer)->out(false));
 251          // Uploaded image takes precedence before gravatar.
 252          $up1 = new user_picture($user1);
 253          $this->assertSame($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/boost/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 254  
 255          // Uploaded image with token-based access for current user.
 256          $up1 = new user_picture($user1);
 257          $up1->includetoken = true;
 258          $token = get_user_key('core_files', $USER->id);
 259          $this->assertSame($CFG->wwwroot.'/tokenpluginfile.php/'.$token.'/'.$context1->id.'/user/icon/boost/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 260  
 261          // Uploaded image with token-based access for other user.
 262          $up1 = new user_picture($user1);
 263          $up1->includetoken = $user2->id;
 264          $token = get_user_key('core_files', $user2->id);
 265          $this->assertSame($CFG->wwwroot.'/tokenpluginfile.php/'.$token.'/'.$context1->id.'/user/icon/boost/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 266  
 267          // Https version.
 268          $CFG->wwwroot = str_replace('http:', 'https:', $CFG->wwwroot);
 269  
 270          $up1 = new user_picture($user1);
 271          $this->assertSame($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/boost/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 272  
 273          $up2 = new user_picture($user2);
 274          $this->assertSame('https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?s=35&d=https%3A%2F%2Fwww.example.com%2Fmoodle%2Fpix%2Fu%2Ff2.png', $up2->get_url($page, $renderer)->out(false));
 275  
 276          $up3 = new user_picture($user3);
 277          $this->assertSame($CFG->wwwroot.'/theme/image.php/boost/core/1/u/f2', $up3->get_url($page, $renderer)->out(false));
 278  
 279          $up4 = new user_picture($user4);
 280          $this->assertSame($CFG->wwwroot.'/theme/image.php/boost/core/1/u/f2', $up4->get_url($page, $renderer)->out(false));
 281  
 282          // TODO MDL-44792 Rewrite those tests to use a fixture.
 283          // Now test gravatar with one theme having own images (afterburner).
 284          // $this->assertFileExists("$CFG->dirroot/theme/afterburner/config.php");
 285          // set_config('theme', 'afterburner');
 286          // $page = new \moodle_page();
 287          // $page->set_url('/user/profile.php');
 288          // $page->set_context(\context_system::instance());
 289          // $renderer = $page->get_renderer('core');
 290  
 291          // $up2 = new user_picture($user2);
 292          // $this->assertEquals('http://www.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?s=35&d=http%3A%2F%2Fwww.example.com%2Fmoodle%2Ftheme%2Fafterburner%2Fpix_core%2Fu%2Ff2.png', $up2->get_url($page, $renderer)->out(false));
 293  
 294          // $up2 = new user_picture($user2);
 295          // $this->assertSame('https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?s=35&d=https%3A%2F%2Fwww.example.com%2Fmoodle%2Ftheme%2Fafterburner%2Fpix_core%2Fu%2Ff2.png', $up2->get_url($page, $renderer)->out(false));
 296          // End of gravatar tests.
 297  
 298          // Test themed images.
 299          // set_config('enablegravatar', 0);
 300          // $this->assertFileExists("$CFG->dirroot/theme/formal_white/config.php"); // Use any other theme.
 301          // set_config('theme', 'formal_white');
 302          // $page = new \moodle_page();
 303          // $page->set_url('/user/profile.php');
 304          // $page->set_context(\context_system::instance());
 305          // $renderer = $page->get_renderer('core');
 306  
 307          // $up1 = new user_picture($user1);
 308          // $this->assertSame($CFG->wwwroot.'/pluginfile.php/'.$context1->id.'/user/icon/formal_white/f2?rev=11', $up1->get_url($page, $renderer)->out(false));
 309  
 310          // $up2 = new user_picture($user2);
 311          // $this->assertSame($CFG->wwwroot.'/theme/image.php/formal_white/core/1/u/f2', $up2->get_url($page, $renderer)->out(false));
 312  
 313          // Test non-slashargument images.
 314          set_config('theme', 'classic');
 315          $CFG->wwwroot = str_replace('https:', 'http:', $CFG->wwwroot);
 316          $CFG->slasharguments = 0;
 317          $page = new \moodle_page();
 318          $page->set_url('/user/profile.php');
 319          $page->set_context(\context_system::instance());
 320          $renderer = $page->get_renderer('core');
 321  
 322          $up3 = new user_picture($user3);
 323          $this->assertSame($CFG->wwwroot.'/theme/image.php?theme=classic&component=core&rev=1&image=u%2Ff2', $up3->get_url($page, $renderer)->out(false));
 324      }
 325  
 326      public function test_empty_menu() {
 327          $emptymenu = new custom_menu();
 328          $this->assertInstanceOf(custom_menu::class, $emptymenu);
 329          $this->assertFalse($emptymenu->has_children());
 330      }
 331  
 332      public function test_basic_syntax() {
 333          $definition = <<<EOF
 334  Moodle community|http://moodle.org
 335  -Moodle free support|http://moodle.org/support
 336  -Moodle development|http://moodle.org/development
 337  --Moodle Tracker|http://tracker.moodle.org
 338  --Moodle Docs|http://docs.moodle.org
 339  -Moodle News|http://moodle.org/news
 340  Moodle company||Moodle trust pty
 341  -Hosting|http://moodle.com/hosting|Commercial hosting
 342  -Support|http://moodle.com/support|Commercial support
 343  EOF;
 344  
 345          $menu = new custom_menu($definition);
 346          $this->assertInstanceOf('custom_menu', $menu);
 347          $this->assertTrue($menu->has_children());
 348          $firstlevel = $menu->get_children();
 349          $this->assertTrue(is_array($firstlevel));
 350          $this->assertCount(2, $firstlevel);
 351  
 352          $item = array_shift($firstlevel);
 353          $this->assertInstanceOf('custom_menu_item', $item);
 354          $this->assertTrue($item->has_children());
 355          $this->assertCount(3, $item->get_children());
 356          $this->assertEquals('Moodle community', $item->get_text());
 357          $itemurl = $item->get_url();
 358          $this->assertTrue($itemurl instanceof \moodle_url);
 359          $this->assertEquals('http://moodle.org', $itemurl->out());
 360          $this->assertNull($item->get_title()); // Implicit title.
 361  
 362          /** @var custom_menu_item $item */
 363          $item = array_shift($firstlevel);
 364          $this->assertTrue($item->has_children());
 365          $this->assertCount(2, $item->get_children());
 366          $this->assertSame('Moodle company', $item->get_text());
 367          $this->assertNull($item->get_url());
 368          $this->assertSame('Moodle trust pty', $item->get_title());
 369  
 370          $children = $item->get_children();
 371          $subitem = array_shift($children);
 372          $this->assertFalse($subitem->has_children());
 373          $this->assertSame('Hosting', $subitem->get_text());
 374          $this->assertSame('Commercial hosting', $subitem->get_title());
 375      }
 376  
 377      public function test_custommenu_mulitlang() {
 378          $definition = <<<EOF
 379  Start|http://school.info
 380  Info
 381  -English|http://school.info/en|Information in English|en
 382  --Nested under English
 383  --I will be lost|||de
 384  -Deutsch|http://school.info/de|Informationen in deutscher Sprache|de,de_du,de_kids
 385  --Nested under Deutsch
 386  --I will be lost|||en
 387  kontaktieren Sie uns|contactus.php||de
 388  Contact us|contactus.php||en
 389  EOF;
 390          $definitionen = <<<EOF
 391  Start|http://school.info
 392  Info
 393  -English|http://school.info/en|Information in English|en
 394  --Nested under English
 395  Contact us|contactus.php||en
 396  EOF;
 397          $definitionde = <<<EOF
 398  Start|http://school.info
 399  Info
 400  -Deutsch|http://school.info/de|Informationen in deutscher Sprache|de,de_du,de_kids
 401  --Nested under Deutsch
 402  kontaktieren Sie uns|contactus.php||de
 403  EOF;
 404  
 405          $definitiondedu = <<<EOF
 406  Start|http://school.info
 407  Info
 408  -Deutsch|http://school.info/de|Informationen in deutscher Sprache|de,de_du,de_kids
 409  --Nested under Deutsch
 410  EOF;
 411  
 412          $parsed = $this->custommenu_out(new custom_menu($definition));
 413          $parseden = $this->custommenu_out(new custom_menu($definition, 'en'));
 414          $parsedde = $this->custommenu_out(new custom_menu($definition, 'de'));
 415          $parseddedu = $this->custommenu_out(new custom_menu($definition, 'de_du'));
 416  
 417          $actualen = $this->custommenu_out(new custom_menu($definitionen, 'en'));
 418          $actualde = $this->custommenu_out(new custom_menu($definitionde, 'de'));
 419          $actualdedu = $this->custommenu_out(new custom_menu($definitiondedu, 'de_du'));
 420  
 421          $this->assertSame($actualen, $parseden, 'The parsed English menu does not match the expected English menu');
 422          $this->assertSame($actualde, $parsedde, 'The parsed German menu does not match the expected German menu');
 423          $this->assertSame($actualdedu, $parseddedu, 'The parsed German [Du] menu does not match the expected German [Du] menu');
 424  
 425          $this->assertNotSame($parsed, $parsedde, 'The menu without language is the same as the German menu. They should differ!');
 426          $this->assertNotSame($parsed, $parseden, 'The menu without language is the same as the English menu. They should differ!');
 427          $this->assertNotSame($parsed, $parseddedu, 'The menu without language is the same as the German [Du] menu. They should differ!');
 428          $this->assertNotSame($parseden, $parsedde, 'The English menu is the same as the German menu. They should differ!');
 429          $this->assertNotSame($parseden, $parseddedu, 'The English menu is the same as the German [Du] menu. They should differ!');
 430          $this->assertNotSame($parseddedu, $parsedde, 'The German [Du] menu is the same as the German menu. They should differ!');
 431      }
 432  
 433      /**
 434       * Support function that takes a custom_menu_item and converts it to a string.
 435       *
 436       * @param custom_menu_item $item
 437       * @param int $depth
 438       * @return string
 439       */
 440      protected function custommenu_out(custom_menu_item $item, $depth = 0) {
 441          $str = str_repeat('-', $depth);
 442          $str .= $item->get_text();
 443          $str .= '|' . $item->get_url();
 444          $str .= '|' . $item->get_title();
 445          if ($item->has_children()) {
 446              $str .= '|' . count($item->get_children());
 447              foreach ($item->get_children() as $child) {
 448                  $str .= "\n" . $this->custommenu_out($child, $depth + 1);
 449              }
 450          }
 451          return $str;
 452      }
 453  
 454      public function test_prepare() {
 455          $expecteda = array('<span class="current-page">1</span>',
 456                             '<a href="index.php?page=1">2</a>',
 457                             '<a href="index.php?page=2">3</a>',
 458                             '<a href="index.php?page=3">4</a>',
 459                             '<a href="index.php?page=4">5</a>',
 460                             '<a href="index.php?page=5">6</a>',
 461                             '<a href="index.php?page=6">7</a>',
 462                             '<a href="index.php?page=7">8</a>',
 463                             );
 464          $expectedb = array('<a href="page?page=3">4</a>',
 465                             '<a href="page?page=4">5</a>',
 466                             '<span class="current-page">6</span>',
 467                             '<a href="page?page=6">7</a>',
 468                             '<a href="page?page=7">8</a>',
 469                             );
 470  
 471          $mpage = new \moodle_page();
 472          $rbase = new renderer_base($mpage, "/");
 473          $pbara = new paging_bar(40, 0, 5, 'index.php');
 474          $pbara->prepare($rbase, $mpage, "/");
 475          $pbarb = new paging_bar(100, 5, 5, 'page');
 476          $pbarb->maxdisplay = 5;
 477          $pbarb->prepare($rbase, $mpage, "/");
 478  
 479          $this->assertEquals($expecteda, $pbara->pagelinks);
 480          $this->assertEquals($expectedb, $pbarb->pagelinks);
 481      }
 482  
 483      public function test_pix_icon() {
 484          $this->resetAfterTest();
 485  
 486          $page = new \moodle_page();
 487  
 488          set_config('theme', 'boost');
 489          // Need to reset after changing theme.
 490          $page->reset_theme_and_output();
 491          $renderer = $page->get_renderer('core');
 492  
 493          $reason = 'An icon with no alt text is hidden from screenreaders.';
 494          $this->assertStringContainsString('aria-hidden="true"', $renderer->pix_icon('t/print', ''), $reason);
 495  
 496          $reason = 'An icon with alt text is not hidden from screenreaders.';
 497          $this->assertStringNotContainsString('aria-hidden="true"', $renderer->pix_icon('t/print', 'Print'), $reason);
 498  
 499          // Test another theme with a different icon system.
 500          set_config('theme', 'classic');
 501          // Need to reset after changing theme.
 502          $page->reset_theme_and_output();
 503          $renderer = $page->get_renderer('core');
 504  
 505          $reason = 'An icon with no alt text is hidden from screenreaders.';
 506          $this->assertStringContainsString('aria-hidden="true"', $renderer->pix_icon('t/print', ''), $reason);
 507  
 508          $reason = 'An icon with alt text is not hidden from screenreaders.';
 509          $this->assertStringNotContainsString('aria-hidden="true"', $renderer->pix_icon('t/print', 'Print'), $reason);
 510      }
 511  
 512      /**
 513       * Test for checking the template context data for the single_select element.
 514       */
 515      public function test_single_select() {
 516          global $PAGE;
 517  
 518          $fakename = 'fakename';
 519          $fakeclass = 'fakeclass';
 520          $faketitle = 'faketitle';
 521          $fakedisabled = true;
 522          $fakefor = 'fakefor';
 523  
 524          $someid = 'someid';
 525          $realname = 'realname';
 526          $realclass = 'realclass';
 527          $realtitle = 'realtitle';
 528          $realdisabled = false;
 529          $reallabel = 'Some cool label';
 530          $labelclass = 'somelabelclass';
 531          $labelstyle = 'font-weight: bold';
 532  
 533          $dataaction = 'actiondata';
 534          $dataother = 'otherdata';
 535  
 536          $attributes = [
 537              'id' => $someid,
 538              'class' => $fakeclass,
 539              'title' => $faketitle,
 540              'disabled' => $fakedisabled,
 541              'name' => $fakename,
 542              'data-action' => $dataaction,
 543              'data-other' => $dataother,
 544          ];
 545          $labelattributes = [
 546              'for' => $fakefor,
 547              'class' => $labelclass,
 548              'style' => $labelstyle
 549          ];
 550  
 551          $options = [ "Option A", "Option B", "Option C" ];
 552          $nothing = ['' => 'choosedots'];
 553  
 554          $url = new \moodle_url('/');
 555  
 556          $singleselect = new single_select($url, $realname, $options, null, $nothing, 'someformid');
 557          $singleselect->class = $realclass;
 558          $singleselect->tooltip = $realtitle;
 559          $singleselect->disabled = $realdisabled;
 560          $singleselect->attributes = $attributes;
 561          $singleselect->label = $reallabel;
 562          $singleselect->labelattributes = $labelattributes;
 563  
 564          $renderer = $PAGE->get_renderer('core');
 565          $data = $singleselect->export_for_template($renderer);
 566  
 567          $this->assertEquals($realtitle, $data->title);
 568          $this->assertEquals($singleselect->class, $data->classes);
 569          $this->assertEquals($realname, $data->name);
 570          $this->assertEquals($reallabel, $data->label);
 571          $this->assertEquals($realdisabled, $data->disabled);
 572          $this->assertEquals($someid, $data->id);
 573  
 574          // Validate attributes array.
 575          // The following should not be included: id, class, name, disabled.
 576          $this->assertFalse(in_array(['name' => 'id', 'value' => $someid], $data->attributes));
 577          $this->assertFalse(in_array(['name' => 'class', 'value' => $fakeclass], $data->attributes));
 578          $this->assertFalse(in_array(['name' => 'name', 'value' => $fakeclass], $data->attributes));
 579          $this->assertFalse(in_array(['name' => 'disabled', 'value' => $fakedisabled], $data->attributes));
 580          // The rest should be fine.
 581          $this->assertTrue(in_array(['name' => 'data-action', 'value' => $dataaction], $data->attributes));
 582          $this->assertTrue(in_array(['name' => 'data-other', 'value' => $dataother], $data->attributes));
 583  
 584          // Validate label attributes.
 585          // The for attribute should not be included.
 586          $this->assertFalse(in_array(['name' => 'for', 'value' => $someid], $data->labelattributes));
 587          // The rest should be fine.
 588          $this->assertTrue(in_array(['name' => 'class', 'value' => $labelclass], $data->labelattributes));
 589          $this->assertTrue(in_array(['name' => 'style', 'value' => $labelstyle], $data->labelattributes));
 590      }
 591  
 592      /**
 593       * Test for checking the template context data for the url_select element.
 594       */
 595      public function test_url_select() {
 596          global $PAGE;
 597  
 598          $fakename = 'fakename';
 599          $fakeclass = 'fakeclass';
 600          $faketitle = 'faketitle';
 601          $fakedisabled = true;
 602          $fakefor = 'fakefor';
 603  
 604          $someid = 'someid';
 605          $realclass = 'realclass';
 606          $realtitle = 'realtitle';
 607          $realdisabled = false;
 608          $reallabel = 'Some cool label';
 609          $labelclass = 'somelabelclass';
 610          $labelstyle = 'font-weight: bold';
 611  
 612          $dataaction = 'actiondata';
 613          $dataother = 'otherdata';
 614  
 615          $attributes = [
 616              'id' => $someid,
 617              'class' => $fakeclass,
 618              'title' => $faketitle,
 619              'disabled' => $fakedisabled,
 620              'name' => $fakename,
 621              'data-action' => $dataaction,
 622              'data-other' => $dataother,
 623          ];
 624          $labelattributes = [
 625              'for' => $fakefor,
 626              'class' => $labelclass,
 627              'style' => $labelstyle
 628          ];
 629  
 630          $url1 = new \moodle_url("/#a");
 631          $url2 = new \moodle_url("/#b");
 632          $url3 = new \moodle_url("/#c");
 633  
 634          $urls = [
 635              $url1->out() => 'A',
 636              $url2->out() => 'B',
 637              $url3->out() => 'C',
 638          ];
 639          $nothing = ['' => 'choosedots'];
 640  
 641          $urlselect = new url_select($urls, null, $nothing, 'someformid');
 642          $urlselect->class = $realclass;
 643          $urlselect->tooltip = $realtitle;
 644          $urlselect->disabled = $realdisabled;
 645          $urlselect->attributes = $attributes;
 646          $urlselect->label = $reallabel;
 647          $urlselect->labelattributes = $labelattributes;
 648  
 649          $renderer = $PAGE->get_renderer('core');
 650          $data = $urlselect->export_for_template($renderer);
 651  
 652          $this->assertEquals($realtitle, $data->title);
 653          $this->assertEquals($urlselect->class, $data->classes);
 654          $this->assertEquals($reallabel, $data->label);
 655          $this->assertEquals($realdisabled, $data->disabled);
 656          $this->assertEquals($someid, $data->id);
 657  
 658          // Validate attributes array.
 659          // The following should not be included: id, class, name, disabled.
 660          $this->assertFalse(in_array(['name' => 'id', 'value' => $someid], $data->attributes));
 661          $this->assertFalse(in_array(['name' => 'class', 'value' => $fakeclass], $data->attributes));
 662          $this->assertFalse(in_array(['name' => 'name', 'value' => $fakeclass], $data->attributes));
 663          $this->assertFalse(in_array(['name' => 'disabled', 'value' => $fakedisabled], $data->attributes));
 664          // The rest should be fine.
 665          $this->assertTrue(in_array(['name' => 'data-action', 'value' => $dataaction], $data->attributes));
 666          $this->assertTrue(in_array(['name' => 'data-other', 'value' => $dataother], $data->attributes));
 667  
 668          // Validate label attributes.
 669          // The for attribute should not be included.
 670          $this->assertFalse(in_array(['name' => 'for', 'value' => $someid], $data->labelattributes));
 671          // The rest should be fine.
 672          $this->assertTrue(in_array(['name' => 'class', 'value' => $labelclass], $data->labelattributes));
 673          $this->assertTrue(in_array(['name' => 'style', 'value' => $labelstyle], $data->labelattributes));
 674      }
 675  
 676      /**
 677       * Data provider for test_block_contents_is_fake().
 678       *
 679       * @return array
 680       */
 681      public function block_contents_is_fake_provider() {
 682          return [
 683              'Null' => [null, false],
 684              'Not set' => [false, false],
 685              'Fake' => ['_fake', true],
 686              'Real block' => ['activity_modules', false],
 687          ];
 688      }
 689  
 690      /**
 691       * Test block_contents is_fake() method.
 692       *
 693       * @dataProvider block_contents_is_fake_provider
 694       * @param mixed $value Value for the data-block attribute
 695       * @param boolean $expected The expected result
 696       */
 697      public function test_block_contents_is_fake($value, $expected) {
 698          $bc = new block_contents(array());
 699          if ($value !== false) {
 700              $bc->attributes['data-block'] = $value;
 701          }
 702          $this->assertEquals($expected, $bc->is_fake());
 703      }
 704  }