Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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