Search moodle.org's
Developer Documentation

See Release Notes

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

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