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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body