Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

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