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]

   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 flexible_table;
  20  use testable_flexible_table;
  21  
  22  defined('MOODLE_INTERNAL') || die();
  23  
  24  global $CFG;
  25  require_once($CFG->libdir . '/tablelib.php');
  26  require_once($CFG->libdir . '/tests/fixtures/testable_flexible_table.php');
  27  
  28  /**
  29   * Test some of tablelib.
  30   *
  31   * @package    core
  32   * @category   test
  33   * @copyright  2013 Damyon Wiese <damyon@moodle.com>
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class tablelib_test extends \advanced_testcase {
  37      protected function generate_columns($cols) {
  38          $columns = array();
  39          foreach (range(0, $cols - 1) as $j) {
  40              array_push($columns, 'column' . $j);
  41          }
  42          return $columns;
  43      }
  44  
  45      protected function generate_headers($cols) {
  46          $columns = array();
  47          foreach (range(0, $cols - 1) as $j) {
  48              array_push($columns, 'Column ' . $j);
  49          }
  50          return $columns;
  51      }
  52  
  53      protected function generate_data($rows, $cols) {
  54          $data = array();
  55  
  56          foreach (range(0, $rows - 1) as $i) {
  57              $row = array();
  58              foreach (range(0, $cols - 1) as $j) {
  59                  $val =  'row ' . $i . ' col ' . $j;
  60                  $row['column' . $j] = $val;
  61              }
  62              array_push($data, $row);
  63          }
  64          return $data;
  65      }
  66  
  67      /**
  68       * Create a table with properties as passed in params, add data and output html.
  69       *
  70       * @param string[] $columns The columns of the table.
  71       * @param string[] $headers The header of the table.
  72       * @param bool $sortable Sorting of the table.
  73       * @param bool $collapsible Is table collapsible.
  74       * @param string[] $suppress Suppress columns.
  75       * @param string[] $nosorting No sorting.
  76       * @param (array|object)[] $data The data of the table.
  77       * @param int $pagesize Page size of the table
  78       * @param string $caption Caption of the table.
  79       * @param array $captionattribute The attribute of the caption.
  80       */
  81      protected function run_table_test($columns, $headers, $sortable, $collapsible, $suppress, $nosorting, $data,
  82          $pagesize, $caption = '', $captionattribute = []) {
  83          $table = $this->create_and_setup_table($columns, $headers, $sortable, $collapsible, $suppress, $nosorting,
  84              $caption, $captionattribute);
  85          $table->pagesize($pagesize, count($data));
  86          foreach ($data as $row) {
  87              $table->add_data_keyed($row);
  88          }
  89          $table->finish_output();
  90      }
  91  
  92      /**
  93       * Create a table with properties as passed in params.
  94       *
  95       * @param string[] $columns The columns of the table.
  96       * @param string[] $headers The header of the table.
  97       * @param bool $sortable Sorting of the table.
  98       * @param bool $collapsible Is table collapsible.
  99       * @param string[] $suppress Suppress columns.
 100       * @param string[] $nosorting No sorting.
 101       * @param string $caption Caption of the table.
 102       * @param array $captionattribute The attribute of the caption.
 103       * @return flexible_table
 104       */
 105      protected function create_and_setup_table($columns, $headers, $sortable, $collapsible, $suppress, $nosorting,
 106          $caption = '', $captionattribute = '') {
 107          $table = new flexible_table('tablelib_test');
 108  
 109          $table->define_columns($columns);
 110          $table->define_headers($headers);
 111          $table->define_baseurl('/invalid.php');
 112  
 113          $table->sortable($sortable);
 114          $table->collapsible($collapsible);
 115          foreach ($suppress as $column) {
 116              $table->column_suppress($column);
 117          }
 118  
 119          foreach ($nosorting as $column) {
 120              $table->no_sorting($column);
 121          }
 122          if ($caption) {
 123              $table->set_caption($caption, $captionattribute);
 124          }
 125  
 126          $table->setup();
 127          return $table;
 128      }
 129  
 130      public function test_empty_table() {
 131          $this->expectOutputRegex('/' . get_string('nothingtodisplay') . '/');
 132          $this->run_table_test(
 133              array('column1', 'column2'),       // Columns.
 134              array('Column 1', 'Column 2'),     // Headers.
 135              true,                              // Sortable.
 136              false,                             // Collapsible.
 137              array(),                           // Suppress columns.
 138              array(),                           // No sorting.
 139              array(),                           // Data.
 140              10                                 // Page size.
 141          );
 142      }
 143  
 144      public function test_has_next_pagination() {
 145  
 146          $data = $this->generate_data(11, 2);
 147          $columns = $this->generate_columns(2);
 148          $headers = $this->generate_headers(2);
 149  
 150          // Search for pagination controls containing 'page-link"\saria-label="Next"'.
 151          $this->expectOutputRegex('/Next page/');
 152  
 153          $this->run_table_test(
 154              $columns,
 155              $headers,
 156              true,
 157              false,
 158              array(),
 159              array(),
 160              $data,
 161              10
 162          );
 163      }
 164  
 165      public function test_has_hide() {
 166  
 167          $data = $this->generate_data(11, 2);
 168          $columns = $this->generate_columns(2);
 169          $headers = $this->generate_headers(2);
 170  
 171          // Search for 'hide' links in the column headers.
 172          $this->expectOutputRegex('/' . get_string('hide') . '/');
 173  
 174          $this->run_table_test(
 175              $columns,
 176              $headers,
 177              true,
 178              true,
 179              array(),
 180              array(),
 181              $data,
 182              10
 183          );
 184      }
 185  
 186      public function test_has_not_hide() {
 187  
 188          $data = $this->generate_data(11, 2);
 189          $columns = $this->generate_columns(2);
 190          $headers = $this->generate_headers(2);
 191  
 192          // Make sure there are no 'hide' links in the headers.
 193  
 194          ob_start();
 195          $this->run_table_test(
 196              $columns,
 197              $headers,
 198              true,
 199              false,
 200              array(),
 201              array(),
 202              $data,
 203              10
 204          );
 205          $output = ob_get_contents();
 206          ob_end_clean();
 207          $this->assertStringNotContainsString(get_string('hide'), $output);
 208      }
 209  
 210      public function test_has_sort() {
 211  
 212          $data = $this->generate_data(11, 2);
 213          $columns = $this->generate_columns(2);
 214          $headers = $this->generate_headers(2);
 215  
 216          // Search for pagination controls containing '1.*2</a>.*Next</a>'.
 217          $this->expectOutputRegex('/' . get_string('sortby') . '/');
 218  
 219          $this->run_table_test(
 220              $columns,
 221              $headers,
 222              true,
 223              false,
 224              array(),
 225              array(),
 226              $data,
 227              10
 228          );
 229      }
 230  
 231      public function test_has_not_sort() {
 232  
 233          $data = $this->generate_data(11, 2);
 234          $columns = $this->generate_columns(2);
 235          $headers = $this->generate_headers(2);
 236  
 237          // Make sure there are no 'Sort by' links in the headers.
 238  
 239          ob_start();
 240          $this->run_table_test(
 241              $columns,
 242              $headers,
 243              false,
 244              false,
 245              array(),
 246              array(),
 247              $data,
 248              10
 249          );
 250          $output = ob_get_contents();
 251          ob_end_clean();
 252          $this->assertStringNotContainsString(get_string('sortby'), $output);
 253      }
 254  
 255      public function test_has_not_next_pagination() {
 256  
 257          $data = $this->generate_data(10, 2);
 258          $columns = $this->generate_columns(2);
 259          $headers = $this->generate_headers(2);
 260  
 261          // Make sure there are no 'Next' links in the pagination.
 262  
 263          ob_start();
 264          $this->run_table_test(
 265              $columns,
 266              $headers,
 267              true,
 268              false,
 269              array(),
 270              array(),
 271              $data,
 272              10
 273          );
 274  
 275          $output = ob_get_contents();
 276          ob_end_clean();
 277          $this->assertStringNotContainsString(get_string('next'), $output);
 278      }
 279  
 280      public function test_1_col() {
 281  
 282          $data = $this->generate_data(100, 1);
 283          $columns = $this->generate_columns(1);
 284          $headers = $this->generate_headers(1);
 285  
 286          $this->expectOutputRegex('/row 0 col 0/');
 287  
 288          $this->run_table_test(
 289              $columns,
 290              $headers,
 291              true,
 292              false,
 293              array(),
 294              array(),
 295              $data,
 296              10
 297          );
 298      }
 299  
 300      public function test_empty_rows() {
 301  
 302          $data = $this->generate_data(1, 5);
 303          $columns = $this->generate_columns(5);
 304          $headers = $this->generate_headers(5);
 305  
 306          // Test that we have at least 5 columns generated for each empty row.
 307          $this->expectOutputRegex('/emptyrow.*r9_c4/');
 308  
 309          $this->run_table_test(
 310              $columns,
 311              $headers,
 312              true,
 313              false,
 314              array(),
 315              array(),
 316              $data,
 317              10
 318          );
 319      }
 320  
 321      public function test_5_cols() {
 322  
 323          $data = $this->generate_data(100, 5);
 324          $columns = $this->generate_columns(5);
 325          $headers = $this->generate_headers(5);
 326  
 327          $this->expectOutputRegex('/row 0 col 0/');
 328  
 329          $this->run_table_test(
 330              $columns,
 331              $headers,
 332              true,
 333              false,
 334              array(),
 335              array(),
 336              $data,
 337              10
 338          );
 339      }
 340  
 341      public function test_50_cols() {
 342  
 343          $data = $this->generate_data(100, 50);
 344          $columns = $this->generate_columns(50);
 345          $headers = $this->generate_headers(50);
 346  
 347          $this->expectOutputRegex('/row 0 col 0/');
 348  
 349          $this->run_table_test(
 350              $columns,
 351              $headers,
 352              true,
 353              false,
 354              array(),
 355              array(),
 356              $data,
 357              10
 358          );
 359      }
 360  
 361      /**
 362       * Data provider for test_fullname_column
 363       *
 364       * @return array
 365       */
 366      public function fullname_column_provider() {
 367          return [
 368              ['language'],
 369              ['alternatename lastname'],
 370              ['firstname lastnamephonetic'],
 371          ];
 372      }
 373  
 374      /**
 375       * Test fullname column observes configured alternate fullname format configuration
 376       *
 377       * @param string $format
 378       * @return void
 379       *
 380       * @dataProvider fullname_column_provider
 381       */
 382      public function test_fullname_column(string $format) {
 383          $this->resetAfterTest();
 384          $this->setAdminUser();
 385  
 386          set_config('alternativefullnameformat', $format);
 387  
 388          $user = $this->getDataGenerator()->create_user();
 389  
 390          $table = $this->create_and_setup_table(['fullname'], [], true, false, [], []);
 391          $this->assertStringContainsString(fullname($user, true), $table->format_row($user)['fullname']);
 392      }
 393  
 394      /**
 395       * Test fullname column ignores fullname format configuration for a user with viewfullnames capability prohibited
 396       *
 397       * @param string $format
 398       * @return void
 399       *
 400       * @dataProvider fullname_column_provider
 401       */
 402      public function test_fullname_column_prohibit_viewfullnames(string $format) {
 403          global $DB, $CFG;
 404  
 405          $this->resetAfterTest();
 406  
 407          set_config('alternativefullnameformat', $format);
 408  
 409          $currentuser = $this->getDataGenerator()->create_user();
 410          $this->setUser($currentuser);
 411  
 412          // Prohibit the viewfullnames from the default user role.
 413          $userrole = $DB->get_record('role', ['id' => $CFG->defaultuserroleid]);
 414          role_change_permission($userrole->id, \context_system::instance(), 'moodle/site:viewfullnames', CAP_PROHIBIT);
 415  
 416          $user = $this->getDataGenerator()->create_user();
 417  
 418          $table = $this->create_and_setup_table(['fullname'], [], true, false, [], []);
 419          $this->assertStringContainsString(fullname($user, false), $table->format_row($user)['fullname']);
 420      }
 421  
 422      public function test_get_row_html() {
 423          $data = $this->generate_data(1, 5);
 424          $columns = $this->generate_columns(5);
 425          $headers = $this->generate_headers(5);
 426          $data = array_keys(array_flip($data[0]));
 427  
 428          $table = new flexible_table('tablelib_test');
 429          $table->define_columns($columns);
 430          $table->define_headers($headers);
 431          $table->define_baseurl('/invalid.php');
 432  
 433          $row = $table->get_row_html($data);
 434          $this->assertMatchesRegularExpression('/row 0 col 0/', $row);
 435          $this->assertMatchesRegularExpression('/<tr class=""/', $row);
 436          $this->assertMatchesRegularExpression('/<td class="cell c0"/', $row);
 437      }
 438  
 439      public function test_persistent_table() {
 440          global $SESSION;
 441  
 442          $data = $this->generate_data(5, 5);
 443          $columns = $this->generate_columns(5);
 444          $headers = $this->generate_headers(5);
 445  
 446          // Testing without persistence first to verify that the results are different.
 447          $table1 = new flexible_table('tablelib_test');
 448          $table1->define_columns($columns);
 449          $table1->define_headers($headers);
 450          $table1->define_baseurl('/invalid.php');
 451  
 452          $table1->sortable(true);
 453          $table1->collapsible(true);
 454  
 455          $table1->is_persistent(false);
 456          $_GET['thide'] = 'column0';
 457          $_GET['tsort'] = 'column1';
 458          $_GET['tifirst'] = 'A';
 459          $_GET['tilast'] = 'Z';
 460  
 461          foreach ($data as $row) {
 462              $table1->add_data_keyed($row);
 463          }
 464          $table1->setup();
 465  
 466          // Clear session data between each new table.
 467          unset($SESSION->flextable);
 468  
 469          $table2 = new flexible_table('tablelib_test');
 470          $table2->define_columns($columns);
 471          $table2->define_headers($headers);
 472          $table2->define_baseurl('/invalid.php');
 473  
 474          $table2->sortable(true);
 475          $table2->collapsible(true);
 476  
 477          $table2->is_persistent(false);
 478          unset($_GET);
 479  
 480          foreach ($data as $row) {
 481              $table2->add_data_keyed($row);
 482          }
 483          $table2->setup();
 484  
 485          $this->assertNotEquals($table1, $table2);
 486  
 487          unset($SESSION->flextable);
 488  
 489          // Now testing with persistence to check that the tables are the same.
 490          $table3 = new flexible_table('tablelib_test');
 491          $table3->define_columns($columns);
 492          $table3->define_headers($headers);
 493          $table3->define_baseurl('/invalid.php');
 494  
 495          $table3->sortable(true);
 496          $table3->collapsible(true);
 497  
 498          $table3->is_persistent(true);
 499          $_GET['thide'] = 'column0';
 500          $_GET['tsort'] = 'column1';
 501          $_GET['tifirst'] = 'A';
 502          $_GET['tilast'] = 'Z';
 503  
 504          foreach ($data as $row) {
 505              $table3->add_data_keyed($row);
 506          }
 507          $table3->setup();
 508  
 509          unset($SESSION->flextable);
 510  
 511          $table4 = new flexible_table('tablelib_test');
 512          $table4->define_columns($columns);
 513          $table4->define_headers($headers);
 514          $table4->define_baseurl('/invalid.php');
 515  
 516          $table4->sortable(true);
 517          $table4->collapsible(true);
 518  
 519          $table4->is_persistent(true);
 520          unset($_GET);
 521  
 522          foreach ($data as $row) {
 523              $table4->add_data_keyed($row);
 524          }
 525          $table4->setup();
 526  
 527          $this->assertEquals($table3, $table4);
 528  
 529          unset($SESSION->flextable);
 530  
 531          // Finally, another test with no persistence, but without clearing the session data.
 532          $table5 = new flexible_table('tablelib_test');
 533          $table5->define_columns($columns);
 534          $table5->define_headers($headers);
 535          $table5->define_baseurl('/invalid.php');
 536  
 537          $table5->sortable(true);
 538          $table5->collapsible(true);
 539  
 540          $table5->is_persistent(true);
 541          $_GET['thide'] = 'column0';
 542          $_GET['tsort'] = 'column1';
 543          $_GET['tifirst'] = 'A';
 544          $_GET['tilast'] = 'Z';
 545  
 546          foreach ($data as $row) {
 547              $table5->add_data_keyed($row);
 548          }
 549          $table5->setup();
 550  
 551          $table6 = new flexible_table('tablelib_test');
 552          $table6->define_columns($columns);
 553          $table6->define_headers($headers);
 554          $table6->define_baseurl('/invalid.php');
 555  
 556          $table6->sortable(true);
 557          $table6->collapsible(true);
 558  
 559          $table6->is_persistent(true);
 560          unset($_GET);
 561  
 562          foreach ($data as $row) {
 563              $table6->add_data_keyed($row);
 564          }
 565          $table6->setup();
 566  
 567          $this->assertEquals($table5, $table6);
 568      }
 569  
 570      /**
 571       * Helper method for preparing tables instances in {@link self::test_can_be_reset()}.
 572       *
 573       * @param string $tableid
 574       * @return testable_flexible_table
 575       */
 576      protected function prepare_table_for_reset_test($tableid) {
 577          global $SESSION;
 578  
 579          unset($SESSION->flextable[$tableid]);
 580  
 581          $data = $this->generate_data(25, 3);
 582          $columns = array('column0', 'column1', 'column2');
 583          $headers = $this->generate_headers(3);
 584  
 585          $table = new testable_flexible_table($tableid);
 586          $table->define_baseurl('/invalid.php');
 587          $table->define_columns($columns);
 588          $table->define_headers($headers);
 589          $table->collapsible(true);
 590          $table->is_persistent(false);
 591  
 592          return $table;
 593      }
 594  
 595      public function test_can_be_reset() {
 596          // Table in its default state (as if seen for the first time), nothing to reset.
 597          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 598          $table->setup();
 599          $this->assertFalse($table->can_be_reset());
 600  
 601          // Table in its default state with default sorting defined, nothing to reset.
 602          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 603          $table->sortable(true, 'column1', SORT_DESC);
 604          $table->setup();
 605          $this->assertFalse($table->can_be_reset());
 606  
 607          // Table explicitly sorted by the default column & direction, nothing to reset.
 608          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 609          $table->sortable(true, 'column1', SORT_DESC);
 610          $_GET['tsort'] = 'column1';
 611          $_GET['tdir'] = SORT_DESC;
 612          $table->setup();
 613          unset($_GET['tsort']);
 614          unset($_GET['tdir']);
 615          $this->assertFalse($table->can_be_reset());
 616  
 617          // Table explicitly sorted twice by the default column & direction, nothing to reset.
 618          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 619          $table->sortable(true, 'column1', SORT_DESC);
 620          $_GET['tsort'] = 'column1';
 621          $_GET['tdir'] = SORT_DESC;
 622          $table->setup();
 623          $table->setup(); // Set up again to simulate the second page request.
 624          unset($_GET['tsort']);
 625          unset($_GET['tdir']);
 626          $this->assertFalse($table->can_be_reset());
 627  
 628          // Table sorted by other than default column, can be reset.
 629          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 630          $table->sortable(true, 'column1', SORT_DESC);
 631          $_GET['tsort'] = 'column2';
 632          $table->setup();
 633          unset($_GET['tsort']);
 634          $this->assertTrue($table->can_be_reset());
 635  
 636          // Table sorted by other than default direction, can be reset.
 637          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 638          $table->sortable(true, 'column1', SORT_DESC);
 639          $_GET['tsort'] = 'column1';
 640          $_GET['tdir'] = SORT_ASC;
 641          $table->setup();
 642          unset($_GET['tsort']);
 643          unset($_GET['tdir']);
 644          $this->assertTrue($table->can_be_reset());
 645  
 646          // Table sorted by the default column after another sorting previously selected.
 647          // This leads to different ORDER BY than just having a single sort defined, can be reset.
 648          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 649          $table->sortable(true, 'column1', SORT_DESC);
 650          $_GET['tsort'] = 'column0';
 651          $table->setup();
 652          $_GET['tsort'] = 'column1';
 653          $table->setup();
 654          unset($_GET['tsort']);
 655          $this->assertTrue($table->can_be_reset());
 656  
 657          // Table having some column collapsed, can be reset.
 658          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 659          $_GET['thide'] = 'column2';
 660          $table->setup();
 661          unset($_GET['thide']);
 662          $this->assertTrue($table->can_be_reset());
 663  
 664          // Table having some column explicitly expanded, nothing to reset.
 665          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 666          $_GET['tshow'] = 'column2';
 667          $table->setup();
 668          unset($_GET['tshow']);
 669          $this->assertFalse($table->can_be_reset());
 670  
 671          // Table after expanding a collapsed column, nothing to reset.
 672          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 673          $_GET['thide'] = 'column0';
 674          $table->setup();
 675          $_GET['tshow'] = 'column0';
 676          $table->setup();
 677          unset($_GET['thide']);
 678          unset($_GET['tshow']);
 679          $this->assertFalse($table->can_be_reset());
 680  
 681          // Table with some name filtering enabled, can be reset.
 682          $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
 683          $_GET['tifirst'] = 'A';
 684          $table->setup();
 685          unset($_GET['tifirst']);
 686          $this->assertTrue($table->can_be_reset());
 687      }
 688  
 689      /**
 690       * Test export in CSV format
 691       */
 692      public function test_table_export() {
 693          $table = new flexible_table('tablelib_test_export');
 694          $table->define_baseurl('/invalid.php');
 695          $table->define_columns(['c1', 'c2', 'c3']);
 696          $table->define_headers(['Col1', 'Col2', 'Col3']);
 697  
 698          ob_start();
 699          $table->is_downloadable(true);
 700          $table->is_downloading('csv');
 701  
 702          $table->setup();
 703          $table->add_data(['column0' => 'a', 'column1' => 'b', 'column2' => 'c']);
 704          $output = ob_get_contents();
 705          ob_end_clean();
 706  
 707          $this->assertEquals("Col1,Col2,Col3\na,b,c\n", substr($output, 3));
 708      }
 709  
 710      /**
 711       * Test the initials functionality.
 712       *
 713       * @dataProvider initials_provider
 714       * @param string|null $getvalue
 715       * @param string|null $setvalue
 716       * @param string|null $finalvalue
 717       */
 718      public function test_initials_first_set(?string $getvalue, ?string $setvalue, ?string $finalvalue): void {
 719          global $_GET;
 720  
 721          $this->resetAfterTest(true);
 722  
 723          $table = new flexible_table('tablelib_test');
 724  
 725          $user = $this->getDataGenerator()->create_user();
 726  
 727          $table->define_columns(['fullname']);
 728          $table->define_headers(['Fullname']);
 729          $table->define_baseurl('/invalid.php');
 730          $table->initialbars(true);
 731  
 732          if ($getvalue !== null) {
 733              $_GET['tifirst'] = $getvalue;
 734          }
 735  
 736          if ($setvalue !== null) {
 737              $table->set_first_initial($setvalue);
 738          }
 739  
 740          $table->setup();
 741  
 742          $this->assertEquals($finalvalue, $table->get_initial_first());
 743      }
 744  
 745      /**
 746       * Test the initials functionality.
 747       *
 748       * @dataProvider initials_provider
 749       * @param string|null $getvalue
 750       * @param string|null $setvalue
 751       * @param string|null $finalvalue
 752       */
 753      public function test_initials_last_set(?string $getvalue, ?string $setvalue, ?string $finalvalue): void {
 754          global $_GET;
 755  
 756          $this->resetAfterTest(true);
 757  
 758          $table = new flexible_table('tablelib_test');
 759  
 760          $user = $this->getDataGenerator()->create_user();
 761  
 762          $table->define_columns(['fullname']);
 763          $table->define_headers(['Fullname']);
 764          $table->define_baseurl('/invalid.php');
 765          $table->initialbars(true);
 766  
 767          if ($getvalue !== null) {
 768              $_GET['tilast'] = $getvalue;
 769          }
 770  
 771          if ($setvalue !== null) {
 772              $table->set_last_initial($setvalue);
 773          }
 774  
 775          $table->setup();
 776  
 777          $this->assertEquals($finalvalue, $table->get_initial_last());
 778      }
 779  
 780      /**
 781       * Data for testing initials providers.
 782       *
 783       * @return array
 784       */
 785      public function initials_provider(): array {
 786          return [
 787              [null, null, null],
 788              ['A', null, 'A'],
 789              ['Z', null, 'Z'],
 790              [null, 'A', 'A'],
 791              [null, 'Z', 'Z'],
 792              ['A', 'Z', 'Z'],
 793              ['Z', 'A', 'A'],
 794          ];
 795      }
 796  
 797      /**
 798       * Data test for set and render caption for table.
 799       *
 800       * @covers ::set_caption_for_table
 801       * @covers ::render_caption_for_table
 802       */
 803      public function test_set_and_render_caption_for_table(): void {
 804          $data = $this->generate_data(10, 2);
 805          $columns = $this->generate_columns(2);
 806          $headers = $this->generate_headers(2);
 807          $caption = 'Caption for table';
 808          $captionattribute = ['class' => 'inline'];
 809          $this->run_table_test(
 810              $columns,
 811              $headers,
 812              // Sortable.
 813              true,
 814              // Collapsible.
 815              false,
 816              // Suppress columns.
 817              [],
 818              // No sorting.
 819              [],
 820              // Data.
 821              $data,
 822              // Page size.
 823              10,
 824              // Caption for table.
 825              $caption,
 826              // Caption attribute.
 827              $captionattribute,
 828          );
 829          $this->expectOutputRegex('/' . '<caption class="inline">' . $caption . '<\/caption>' . '/');
 830      }
 831  
 832  }