2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
22 * @copyright 2013 Damyon Wiese <damyon@moodle.com>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') ||
die();
29 require_once($CFG->libdir
. '/tablelib.php');
30 require_once($CFG->libdir
. '/tests/fixtures/testable_flexible_table.php');
33 * Test some of tablelib.
37 * @copyright 2013 Damyon Wiese <damyon@moodle.com>
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 class core_tablelib_testcase
extends basic_testcase
{
41 protected function generate_columns($cols) {
43 foreach (range(0, $cols - 1) as $j) {
44 array_push($columns, 'column' . $j);
49 protected function generate_headers($cols) {
51 foreach (range(0, $cols - 1) as $j) {
52 array_push($columns, 'Column ' . $j);
57 protected function generate_data($rows, $cols) {
60 foreach (range(0, $rows - 1) as $i) {
62 foreach (range(0, $cols - 1) as $j) {
63 $val = 'row ' . $i . ' col ' . $j;
64 $row['column' . $j] = $val;
66 array_push($data, $row);
72 * Create a table with properties as passed in params, add data and output html.
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
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);
89 $table->finish_output();
93 * Create a table with properties as passed in params.
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
103 protected function create_and_setup_table($columns, $headers, $sortable, $collapsible, $suppress, $nosorting) {
104 $table = new flexible_table('tablelib_test');
106 $table->define_columns($columns);
107 $table->define_headers($headers);
108 $table->define_baseurl('/invalid.php');
110 $table->sortable($sortable);
111 $table->collapsible($collapsible);
112 foreach ($suppress as $column) {
113 $table->column_suppress($column);
116 foreach ($nosorting as $column) {
117 $table->no_sorting($column);
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.
130 false, // Collapsible.
131 array(), // Suppress columns.
132 array(), // No sorting.
138 public function test_has_next_pagination() {
140 $data = $this->generate_data(11, 2);
141 $columns = $this->generate_columns(2);
142 $headers = $this->generate_headers(2);
144 // Search for pagination controls containing '1.*2</a>.*Next</a>'.
145 $this->expectOutputRegex('/1.*2<\/a>.*' . get_string('next') . '<\/a>/');
147 $this->run_table_test(
159 public function test_has_hide() {
161 $data = $this->generate_data(11, 2);
162 $columns = $this->generate_columns(2);
163 $headers = $this->generate_headers(2);
165 // Search for 'hide' links in the column headers.
166 $this->expectOutputRegex('/' . get_string('hide') . '/');
168 $this->run_table_test(
180 public function test_has_not_hide() {
182 $data = $this->generate_data(11, 2);
183 $columns = $this->generate_columns(2);
184 $headers = $this->generate_headers(2);
186 // Make sure there are no 'hide' links in the headers.
189 $this->run_table_test(
199 $output = ob_get_contents();
201 $this->assertNotContains(get_string('hide'), $output);
204 public function test_has_sort() {
206 $data = $this->generate_data(11, 2);
207 $columns = $this->generate_columns(2);
208 $headers = $this->generate_headers(2);
210 // Search for pagination controls containing '1.*2</a>.*Next</a>'.
211 $this->expectOutputRegex('/' . get_string('sortby') . '/');
213 $this->run_table_test(
225 public function test_has_not_sort() {
227 $data = $this->generate_data(11, 2);
228 $columns = $this->generate_columns(2);
229 $headers = $this->generate_headers(2);
231 // Make sure there are no 'Sort by' links in the headers.
234 $this->run_table_test(
244 $output = ob_get_contents();
246 $this->assertNotContains(get_string('sortby'), $output);
249 public function test_has_not_next_pagination() {
251 $data = $this->generate_data(10, 2);
252 $columns = $this->generate_columns(2);
253 $headers = $this->generate_headers(2);
255 // Make sure there are no 'Next' links in the pagination.
258 $this->run_table_test(
269 $output = ob_get_contents();
271 $this->assertNotContains(get_string('next'), $output);
274 public function test_1_col() {
276 $data = $this->generate_data(100, 1);
277 $columns = $this->generate_columns(1);
278 $headers = $this->generate_headers(1);
280 $this->expectOutputRegex('/row 0 col 0/');
282 $this->run_table_test(
294 public function test_empty_rows() {
296 $data = $this->generate_data(1, 5);
297 $columns = $this->generate_columns(5);
298 $headers = $this->generate_headers(5);
300 // Test that we have at least 5 columns generated for each empty row.
301 $this->expectOutputRegex('/emptyrow.*r9_c4/');
303 $this->run_table_test(
315 public function test_5_cols() {
317 $data = $this->generate_data(100, 5);
318 $columns = $this->generate_columns(5);
319 $headers = $this->generate_headers(5);
321 $this->expectOutputRegex('/row 0 col 0/');
323 $this->run_table_test(
335 public function test_50_cols() {
337 $data = $this->generate_data(100, 50);
338 $columns = $this->generate_columns(50);
339 $headers = $this->generate_headers(50);
341 $this->expectOutputRegex('/row 0 col 0/');
343 $this->run_table_test(
355 public function test_get_row_html() {
356 $data = $this->generate_data(1, 5);
357 $columns = $this->generate_columns(5);
358 $headers = $this->generate_headers(5);
359 $data = array_keys(array_flip($data[0]));
361 $table = new flexible_table('tablelib_test');
362 $table->define_columns($columns);
363 $table->define_headers($headers);
364 $table->define_baseurl('/invalid.php');
366 $row = $table->get_row_html($data);
367 $this->assertRegExp('/row 0 col 0/', $row);
368 $this->assertRegExp('/<tr class=""/', $row);
369 $this->assertRegExp('/<td class="cell c0"/', $row);
372 public function test_persistent_table() {
375 $data = $this->generate_data(5, 5);
376 $columns = $this->generate_columns(5);
377 $headers = $this->generate_headers(5);
379 // Testing without persistence first to verify that the results are different.
380 $table1 = new flexible_table('tablelib_test');
381 $table1->define_columns($columns);
382 $table1->define_headers($headers);
383 $table1->define_baseurl('/invalid.php');
385 $table1->sortable(true);
386 $table1->collapsible(true);
388 $table1->is_persistent(false);
389 $_GET['thide'] = 'column0';
390 $_GET['tsort'] = 'column1';
391 $_GET['tifirst'] = 'A';
392 $_GET['tilast'] = 'Z';
394 foreach ($data as $row) {
395 $table1->add_data_keyed($row);
399 // Clear session data between each new table.
400 unset($SESSION->flextable
);
402 $table2 = new flexible_table('tablelib_test');
403 $table2->define_columns($columns);
404 $table2->define_headers($headers);
405 $table2->define_baseurl('/invalid.php');
407 $table2->sortable(true);
408 $table2->collapsible(true);
410 $table2->is_persistent(false);
413 foreach ($data as $row) {
414 $table2->add_data_keyed($row);
418 $this->assertNotEquals($table1, $table2);
420 unset($SESSION->flextable
);
422 // Now testing with persistence to check that the tables are the same.
423 $table3 = new flexible_table('tablelib_test');
424 $table3->define_columns($columns);
425 $table3->define_headers($headers);
426 $table3->define_baseurl('/invalid.php');
428 $table3->sortable(true);
429 $table3->collapsible(true);
431 $table3->is_persistent(true);
432 $_GET['thide'] = 'column0';
433 $_GET['tsort'] = 'column1';
434 $_GET['tifirst'] = 'A';
435 $_GET['tilast'] = 'Z';
437 foreach ($data as $row) {
438 $table3->add_data_keyed($row);
442 unset($SESSION->flextable
);
444 $table4 = new flexible_table('tablelib_test');
445 $table4->define_columns($columns);
446 $table4->define_headers($headers);
447 $table4->define_baseurl('/invalid.php');
449 $table4->sortable(true);
450 $table4->collapsible(true);
452 $table4->is_persistent(true);
455 foreach ($data as $row) {
456 $table4->add_data_keyed($row);
460 $this->assertEquals($table3, $table4);
462 unset($SESSION->flextable
);
464 // Finally, another test with no persistence, but without clearing the session data.
465 $table5 = new flexible_table('tablelib_test');
466 $table5->define_columns($columns);
467 $table5->define_headers($headers);
468 $table5->define_baseurl('/invalid.php');
470 $table5->sortable(true);
471 $table5->collapsible(true);
473 $table5->is_persistent(true);
474 $_GET['thide'] = 'column0';
475 $_GET['tsort'] = 'column1';
476 $_GET['tifirst'] = 'A';
477 $_GET['tilast'] = 'Z';
479 foreach ($data as $row) {
480 $table5->add_data_keyed($row);
484 $table6 = new flexible_table('tablelib_test');
485 $table6->define_columns($columns);
486 $table6->define_headers($headers);
487 $table6->define_baseurl('/invalid.php');
489 $table6->sortable(true);
490 $table6->collapsible(true);
492 $table6->is_persistent(true);
495 foreach ($data as $row) {
496 $table6->add_data_keyed($row);
500 $this->assertEquals($table5, $table6);
504 * Helper method for preparing tables instances in {@link self::test_can_be_reset()}.
506 * @param string $tableid
507 * @return testable_flexible_table
509 protected function prepare_table_for_reset_test($tableid) {
512 unset($SESSION->flextable
[$tableid]);
514 $data = $this->generate_data(25, 3);
515 $columns = array('column0', 'column1', 'column2');
516 $headers = $this->generate_headers(3);
518 $table = new testable_flexible_table($tableid);
519 $table->define_baseurl('/invalid.php');
520 $table->define_columns($columns);
521 $table->define_headers($headers);
522 $table->collapsible(true);
523 $table->is_persistent(false);
528 public function test_can_be_reset() {
530 // Table in its default state (as if seen for the first time), nothing to reset.
531 $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
533 $this->assertFalse($table->can_be_reset());
535 // Table in its default state with default sorting defined, nothing to reset.
536 $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
537 $table->sortable(true, 'column1', SORT_DESC
);
539 $this->assertFalse($table->can_be_reset());
541 // Table explicitly sorted by the default column (reverses the order), can be reset.
542 $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
543 $table->sortable(true, 'column1', SORT_DESC
);
544 $_GET['tsort'] = 'column1';
546 unset($_GET['tsort']);
547 $this->assertTrue($table->can_be_reset());
549 // Table explicitly sorted twice by the default column (puts back to default order), nothing to reset.
550 $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
551 $table->sortable(true, 'column1', SORT_DESC
);
552 $_GET['tsort'] = 'column1';
554 $table->setup(); // Set up again to simulate the second page request.
555 unset($_GET['tsort']);
556 $this->assertFalse($table->can_be_reset());
558 // Table sorted by other than default column, can be reset.
559 $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
560 $table->sortable(true, 'column1', SORT_DESC
);
561 $_GET['tsort'] = 'column2';
563 unset($_GET['tsort']);
564 $this->assertTrue($table->can_be_reset());
566 // Table sorted by the default column after another sorting previously selected.
567 // This leads to different ORDER BY than just having a single sort defined, can be reset.
568 $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
569 $table->sortable(true, 'column1', SORT_DESC
);
570 $_GET['tsort'] = 'column0';
572 $_GET['tsort'] = 'column1';
574 unset($_GET['tsort']);
575 $this->assertTrue($table->can_be_reset());
577 // Table having some column collapsed, can be reset.
578 $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
579 $_GET['thide'] = 'column2';
581 unset($_GET['thide']);
582 $this->assertTrue($table->can_be_reset());
584 // Table having some column explicitly expanded, nothing to reset.
585 $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
586 $_GET['tshow'] = 'column2';
588 unset($_GET['tshow']);
589 $this->assertFalse($table->can_be_reset());
591 // Table after expanding a collapsed column, nothing to reset.
592 $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
593 $_GET['thide'] = 'column0';
595 $_GET['tshow'] = 'column0';
597 unset($_GET['thide']);
598 unset($_GET['tshow']);
599 $this->assertFalse($table->can_be_reset());
601 // Table with some name filtering enabled, can be reset.
602 $table = $this->prepare_table_for_reset_test(uniqid('tablelib_test_'));
603 $_GET['tifirst'] = 'A';
605 unset($_GET['tifirst']);
606 $this->assertTrue($table->can_be_reset());