Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

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