diff --git a/course/tests/reportbuilder/datasource/courses_test.php b/course/tests/reportbuilder/datasource/courses_test.php index dced044e351..d9011fd0bfa 100644 --- a/course/tests/reportbuilder/datasource/courses_test.php +++ b/course/tests/reportbuilder/datasource/courses_test.php @@ -21,7 +21,11 @@ namespace core_course\reportbuilder\datasource; use core_customfield_generator; use core_reportbuilder_testcase; use core_reportbuilder_generator; +use core_reportbuilder\local\filters\boolean_select; +use core_reportbuilder\local\filters\date; +use core_reportbuilder\local\filters\select; use core_reportbuilder\local\filters\tags; +use core_reportbuilder\local\filters\text; defined('MOODLE_INTERNAL') || die(); @@ -60,12 +64,13 @@ class courses_test extends core_reportbuilder_testcase { $content = $this->get_custom_report_content($report->get('id')); $this->assertCount(1, $content); - $contentrow = array_values(reset($content)); + $contentrow = array_values($content[0]); + $this->assertEquals([ - 'My cats', // Category name. - 'C101', // Course shortname. - 'All about cats', // Course fullname. - 'CAT101', // Course ID number. + $category->get_formatted_name(), + $course->shortname, + $course->fullname, + $course->idnumber, ], $contentrow); } @@ -75,17 +80,44 @@ class courses_test extends core_reportbuilder_testcase { public function test_datasource_non_default_columns(): void { $this->resetAfterTest(); - $course = $this->getDataGenerator()->create_course(['tags' => ['Horses']]); + $category = $this->getDataGenerator()->create_category([ + 'name' => 'Animals', + 'idnumber' => 'CAT101', + 'description' => 'Category description', + ]); + $course = $this->getDataGenerator()->create_course([ + 'category' => $category->id, + 'fullname' => 'Cats', + 'summary' => 'Course description', + 'tags' => ['Horses'], + ]); /** @var core_reportbuilder_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); $report = $generator->create_report(['name' => 'Courses', 'source' => courses::class, 'default' => 0]); // Category. + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course_category:namewithlink']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course_category:path']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course_category:idnumber']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course_category:description']); // Course. - $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:coursefullnamewithlink']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:courseshortnamewithlink']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:courseidnumberewithlink']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:summary']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:format']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:startdate']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:enddate']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:visible']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:groupmode']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:groupmodeforce']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:lang']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:calendartype']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:theme']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:enablecompletion']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:downloadcontent']); // Tags. $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:name']); @@ -95,10 +127,33 @@ class courses_test extends core_reportbuilder_testcase { $this->assertCount(1, $content); $courserow = array_values($content[0]); - $this->assertEquals('Category 1', $courserow[0]); - $this->assertEquals($course->fullname, $courserow[1]); - $this->assertEquals('Horses', $courserow[2]); - $this->assertStringContainsString('Horses', $courserow[3]); + + // Category. + $this->assertStringContainsString($category->get_formatted_name(), $courserow[0]); + $this->assertEquals($category->get_nested_name(false), $courserow[1]); + $this->assertEquals($category->idnumber, $courserow[2]); + $this->assertEquals(format_text($category->description, $category->descriptionformat), $courserow[3]); + + // Course. + $this->assertStringContainsString($course->fullname, $courserow[4]); + $this->assertStringContainsString($course->shortname, $courserow[5]); + $this->assertStringContainsString($course->idnumber, $courserow[6]); + $this->assertEquals(format_text($course->summary, $course->summaryformat), $courserow[7]); + $this->assertEquals('Topics format', $courserow[8]); + $this->assertEquals(userdate($course->startdate), $courserow[9]); + $this->assertEmpty($courserow[10]); + $this->assertEquals('Yes', $courserow[11]); + $this->assertEquals('No groups', $courserow[12]); + $this->assertEquals('No', $courserow[13]); + $this->assertEmpty($courserow[14]); + $this->assertEmpty($courserow[15]); + $this->assertEmpty($courserow[16]); + $this->assertEquals('No', $courserow[17]); + $this->assertEmpty($courserow[18]); + + // Tags. + $this->assertEquals('Horses', $courserow[19]); + $this->assertStringContainsString('Horses', $courserow[20]); } /** @@ -146,6 +201,142 @@ class courses_test extends core_reportbuilder_testcase { */ public function datasource_filters_provider(): array { return [ + // Category. + 'Filter category' => ['course_category:name', [ + 'course_category:name_value' => -1, + ], false], + 'Filter category name' => ['course_category:text', [ + 'course_category:text_operator' => text::IS_EQUAL_TO, + 'course_category:text_value' => 'Animals', + ], true], + 'Filter category name (no match)' => ['course_category:text', [ + 'course_category:text_operator' => text::IS_EQUAL_TO, + 'course_category:text_value' => 'Fruit', + ], false], + 'Filter category idnumber' => ['course_category:idnumber', [ + 'course_category:idnumber_operator' => text::IS_EQUAL_TO, + 'course_category:idnumber_value' => 'CAT101', + ], true], + 'Filter category idnumber (no match)' => ['course_category:idnumber', [ + 'course_category:idnumber_operator' => text::CONTAINS, + 'course_category:idnumber_value' => 'FRUIT', + ], false], + + // Course. + 'Filter course' => ['course:courseselector', [ + 'course:courseselector_values' => [-1], + ], false], + 'Filter course fullname' => ['course:fullname', [ + 'course:fullname_operator' => text::IS_EQUAL_TO, + 'course:fullname_value' => 'Equine', + ], true], + 'Filter course fullname (no match)' => ['course:fullname', [ + 'course:fullname_operator' => text::IS_EQUAL_TO, + 'course:fullname_value' => 'Foxes', + ], false], + 'Filter course shortname' => ['course:shortname', [ + 'course:shortname_operator' => text::IS_EQUAL_TO, + 'course:shortname_value' => 'EQ101', + ], true], + 'Filter course shortname (no match)' => ['course:shortname', [ + 'course:shortname_operator' => text::IS_EQUAL_TO, + 'course:shortname_value' => 'FX101', + ], false], + 'Filter course idnumber' => ['course:idnumber', [ + 'course:idnumber_operator' => text::IS_EQUAL_TO, + 'course:idnumber_value' => 'E-101AB', + ], true], + 'Filter course idnumber (no match)' => ['course:idnumber', [ + 'course:idnumber_operator' => text::IS_EQUAL_TO, + 'course:idnumber_value' => 'F-101XT', + ], false], + 'Filter course summary' => ['course:summary', [ + 'course:summary_operator' => text::CONTAINS, + 'course:summary_value' => 'Lorem ipsum', + ], true], + 'Filter course summary (no match)' => ['course:summary', [ + 'course:summary_operator' => text::IS_EQUAL_TO, + 'course:summary_value' => 'Fiat', + ], false], + 'Filter course format' => ['course:format', [ + 'course:format_operator' => select::EQUAL_TO, + 'course:format_value' => 'topics', + ], true], + 'Filter course format (no match)' => ['course:format', [ + 'course:format_operator' => select::EQUAL_TO, + 'course:format_value' => 'weekly', + ], false], + 'Filter course startdate' => ['course:startdate', [ + 'course:startdate_operator' => date::DATE_RANGE, + 'course:startdate_from' => 1622502000, + ], true], + 'Filter course startdate (no match)' => ['course:startdate', [ + 'course:startdate_operator' => date::DATE_RANGE, + 'course:startdate_to' => 1622502000, + ], false], + 'Filter course enddate' => ['course:enddate', [ + 'course:enddate_operator' => date::DATE_EMPTY, + ], true], + 'Filter course enddate (no match)' => ['course:enddate', [ + 'course:enddate_operator' => date::DATE_NOT_EMPTY, + ], false], + 'Filter course visible' => ['course:visible', [ + 'course:visible_operator' => boolean_select::CHECKED, + ], true], + 'Filter course visible (no match)' => ['course:visible', [ + 'course:visible_operator' => boolean_select::NOT_CHECKED, + ], false], + 'Filter course groupmode' => ['course:groupmode', [ + 'course:groupmode_operator' => select::EQUAL_TO, + 'course:groupmode_value' => 0, // No groups. + ], true], + 'Filter course groupmode (no match)' => ['course:groupmode', [ + 'course:groupmode_operator' => select::EQUAL_TO, + 'course:groupmode_value' => 1, // Separate groups. + ], false], + 'Filter course groupmodeforce' => ['course:groupmodeforce', [ + 'course:groupmodeforce_operator' => boolean_select::NOT_CHECKED, + ], true], + 'Filter course groupmodeforce (no match)' => ['course:groupmodeforce', [ + 'course:groupmodeforce_operator' => boolean_select::CHECKED, + ], false], + 'Filter course lang' => ['course:lang', [ + 'course:lang_operator' => select::EQUAL_TO, + 'course:lang_value' => 'en', + ], true], + 'Filter course lang (no match)' => ['course:lang', [ + 'course:lang_operator' => select::EQUAL_TO, + 'course:lang_value' => 'de', + ], false], + 'Filter course calendartype' => ['course:calendartype', [ + 'course:calendartype_operator' => select::EQUAL_TO, + 'course:calendartype_value' => 'gregorian', + ], true], + 'Filter course calendartype (no match)' => ['course:calendartype', [ + 'course:calendartype_operator' => select::EQUAL_TO, + 'course:calendartype_value' => 'hijri', + ], false], + 'Filter course theme' => ['course:theme', [ + 'course:theme_operator' => select::EQUAL_TO, + 'course:theme_value' => 'boost', + ], true], + 'Filter course theme (no match)' => ['course:theme', [ + 'course:theme_operator' => select::EQUAL_TO, + 'course:theme_value' => 'classic', + ], false], + 'Filter course enablecompletion' => ['course:enablecompletion', [ + 'course:enablecompletion_operator' => boolean_select::NOT_CHECKED, + ], true], + 'Filter course enablecompletion (no match)' => ['course:enablecompletion', [ + 'course:enablecompletion_operator' => boolean_select::CHECKED, + ], false], + 'Filter course downloadcontent' => ['course:downloadcontent', [ + 'course:downloadcontent_operator' => boolean_select::CHECKED, + ], true], + 'Filter course downloadcontent (no match)' => ['course:downloadcontent', [ + 'course:downloadcontent_operator' => boolean_select::NOT_CHECKED, + ], false], + // Tags. 'Filter tag name' => ['tag:name', [ 'tag:name_operator' => tags::EQUAL_TO, @@ -166,14 +357,21 @@ class courses_test extends core_reportbuilder_testcase { * * @dataProvider datasource_filters_provider */ - public function test_datasource_filters( - string $filtername, - array $filtervalues, - bool $expectmatch - ): void { + public function test_datasource_filters(string $filtername, array $filtervalues, bool $expectmatch): void { $this->resetAfterTest(); - $course = $this->getDataGenerator()->create_course(['tags' => ['Horses']]); + $category = $this->getDataGenerator()->create_category(['name' => 'Animals', 'idnumber' => 'CAT101']); + $course = $this->getDataGenerator()->create_course([ + 'category' => $category->id, + 'fullname' => 'Equine', + 'shortname' => 'EQ101', + 'idnumber' => 'E-101AB', + 'lang' => 'en', + 'calendartype' => 'gregorian', + 'theme' => 'boost', + 'downloadcontent' => 1, + 'tags' => ['Horses'], + ]); /** @var core_reportbuilder_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); diff --git a/reportbuilder/classes/local/helpers/user_profile_fields.php b/reportbuilder/classes/local/helpers/user_profile_fields.php index c3d934ab679..2dc26fc3d44 100644 --- a/reportbuilder/classes/local/helpers/user_profile_fields.php +++ b/reportbuilder/classes/local/helpers/user_profile_fields.php @@ -193,7 +193,7 @@ class user_profile_fields { break; case 'datetime': $classname = date::class; - $fieldsql = $DB->sql_cast_char2int($field); + $fieldsql = $DB->sql_cast_char2int($field, true); break; case 'menu': $classname = select::class; diff --git a/reportbuilder/tests/fixtures/course_entity_report.php b/reportbuilder/tests/fixtures/course_entity_report.php deleted file mode 100644 index 239c0554b10..00000000000 --- a/reportbuilder/tests/fixtures/course_entity_report.php +++ /dev/null @@ -1,76 +0,0 @@ -. - -declare(strict_types=1); - -namespace core_reportbuilder; - -use core_reportbuilder\local\entities\course; -use core_reportbuilder\local\helpers\database; - -/** - * Testable system report fixture for testing the course entity - * - * @package core_reportbuilder - * @copyright 2021 David Matamoros - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class course_entity_report extends system_report { - - /** - * Initialise the report - */ - protected function initialise(): void { - $entity = new course(); - $coursetablealias = $entity->get_table_alias('course'); - $param = database::generate_param_name(); - - $this->set_main_table('course', $coursetablealias); - $this->add_entity($entity); - // Add a base condition to hide the site course. - $this->add_base_condition_sql("$coursetablealias.id <> :$param", [$param => SITEID]); - - $columns = []; - foreach ($entity->get_columns() as $column) { - $columns[] = $column->get_unique_identifier(); - } - $this->add_columns_from_entities($columns); - - $filters = []; - foreach ($entity->get_filters() as $filter) { - $filters[] = $filter->get_unique_identifier(); - } - $this->add_filters_from_entities($filters); - } - - /** - * Ensure we can view the report - * - * @return bool - */ - protected function can_view(): bool { - return true; - } - - /** - * Explicitly set availability of report - * - * @return bool - */ - public static function is_available(): bool { - return true; - } -} diff --git a/reportbuilder/tests/fixtures/user_entity_report.php b/reportbuilder/tests/fixtures/user_entity_report.php deleted file mode 100644 index 721de588a2f..00000000000 --- a/reportbuilder/tests/fixtures/user_entity_report.php +++ /dev/null @@ -1,71 +0,0 @@ -. - -declare(strict_types=1); - -namespace core_reportbuilder; - -use core_reportbuilder\local\entities\user; - -/** - * Testable system report fixture for testing the user entity - * - * @package core_reportbuilder - * @copyright 2021 Paul Holden - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class user_entity_report extends system_report { - - /** - * Initialise the report - */ - protected function initialise(): void { - $entity = new user(); - - $this->set_main_table('user', $entity->get_table_alias('user')); - $this->add_entity($entity); - - $columns = []; - foreach ($entity->get_columns() as $column) { - $columns[] = $column->get_unique_identifier(); - } - $this->add_columns_from_entities($columns); - - $filters = []; - foreach ($entity->get_filters() as $filter) { - $filters[] = $filter->get_unique_identifier(); - } - $this->add_filters_from_entities($filters); - } - - /** - * Ensure we can view the report - * - * @return bool - */ - protected function can_view(): bool { - return true; - } - - /** - * Explicitly set availability of report - * - * @return bool - */ - public static function is_available(): bool { - return true; - } -} diff --git a/reportbuilder/tests/local/entities/base_test.php b/reportbuilder/tests/local/entities/base_test.php new file mode 100644 index 00000000000..603630162a6 --- /dev/null +++ b/reportbuilder/tests/local/entities/base_test.php @@ -0,0 +1,349 @@ +. + +/** + * Unit tests for base entity + * + * @package core_reportbuilder + * @copyright 2021 David Matamoros + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +declare(strict_types=1); + +namespace core_reportbuilder\local\entities; + +use advanced_testcase; +use coding_exception; +use lang_string; +use core_reportbuilder\local\filters\text; +use core_reportbuilder\local\report\column; +use core_reportbuilder\local\report\filter; + +defined('MOODLE_INTERNAL') || die(); + +/** + * Unit tests for base entity + * + * @package core_reportbuilder + * @covers \core_reportbuilder\local\entities\base + * @copyright 2021 David Matamoros + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class base_test extends advanced_testcase { + + /** + * Test entity table alias + */ + public function test_get_table_alias(): void { + $entity = new base_test_entity(); + $this->assertEquals('m', $entity->get_table_alias('mytable')); + } + + /** + * Test for invalid get table alias + */ + public function test_get_table_alias_invalid(): void { + $entity = new base_test_entity(); + + $this->expectException(coding_exception::class); + $this->expectExceptionMessage('Coding error detected, it must be fixed by a programmer: ' . + 'Invalid table name (nonexistingalias)'); + $entity->get_table_alias('nonexistingalias'); + } + + /** + * Test setting table alias + */ + public function test_set_table_alias(): void { + $entity = new base_test_entity(); + + $entity->set_table_alias('mytable', 'newalias'); + $this->assertEquals('newalias', $entity->get_table_alias('mytable')); + } + + /** + * Test invalid entity set table alias + */ + public function test_set_table_alias_invalid(): void { + $entity = new base_test_entity(); + + $this->expectException(coding_exception::class); + $this->expectExceptionMessage('Coding error detected, it must be fixed by a programmer: Invalid table name (nonexistent)'); + $entity->set_table_alias('nonexistent', 'newalias'); + } + + /** + * Test setting multiple table aliases + */ + public function test_set_table_aliases(): void { + $entity = new base_test_entity(); + + $entity->set_table_aliases([ + 'mytable' => 'newalias', + 'myothertable' => 'newalias2', + ]); + $this->assertEquals('newalias', $entity->get_table_alias('mytable')); + $this->assertEquals('newalias2', $entity->get_table_alias('myothertable')); + } + + /** + * Test setting multiple table aliases, containing an invalid table + */ + public function test_set_table_aliases_invalid(): void { + $entity = new base_test_entity(); + + $this->expectException(coding_exception::class); + $this->expectExceptionMessage('Coding error detected, it must be fixed by a programmer: Invalid table name (nonexistent)'); + $entity->set_table_aliases([ + 'mytable' => 'newalias', + 'nonexistent' => 'newalias2', + ]); + } + + /** + * Test entity name + */ + public function test_set_entity_name(): void { + $entity = new base_test_entity(); + + $this->assertEquals('base_test_entity', $entity->get_entity_name()); + + $entity->set_entity_name('newentityname'); + $this->assertEquals('newentityname', $entity->get_entity_name()); + } + + /** + * Test invalid entity name + */ + public function test_set_entity_name_invalid(): void { + $entity = new base_test_entity(); + + $this->expectException(coding_exception::class); + $this->expectExceptionMessage('Entity name must be comprised of alphanumeric character, underscore or dash'); + $entity->set_entity_name(''); + } + + /** + * Test entity title + */ + public function test_set_entity_title(): void { + $entity = new base_test_entity(); + + $this->assertEquals(new lang_string('yes'), $entity->get_entity_title()); + + $newtitle = new lang_string('fullname'); + $entity->set_entity_title($newtitle); + $this->assertEquals($newtitle, $entity->get_entity_title()); + } + + /** + * Test adding single join + */ + public function test_add_join(): void { + $entity = new base_test_entity(); + + $tablejoin = "JOIN {course} c2 ON c2.id = c1.id"; + $entity->add_join($tablejoin); + + $this->assertEquals([$tablejoin], $entity->get_joins()); + } + + /** + * Test adding multiple joins + */ + public function test_add_joins(): void { + $entity = new base_test_entity(); + + $tablejoins = [ + "JOIN {course} c2 ON c2.id = c1.id", + "JOIN {course} c3 ON c3.id = c1.id", + ]; + $entity->add_joins($tablejoins); + + $this->assertEquals($tablejoins, $entity->get_joins()); + } + + /** + * Test adding duplicate joins + */ + public function test_add_duplicate_joins(): void { + $entity = new base_test_entity(); + + $tablejoins = [ + "JOIN {course} c2 ON c2.id = c1.id", + "JOIN {course} c3 ON c3.id = c1.id", + ]; + $entity + ->add_joins($tablejoins) + ->add_joins($tablejoins); + + $this->assertEquals($tablejoins, $entity->get_joins()); + } + + /** + * Test getting column + */ + public function test_get_column(): void { + $entity = (new base_test_entity())->initialise(); + + $column = $entity->get_column('test'); + $this->assertEquals('base_test_entity:test', $column->get_unique_identifier()); + } + + /** + * Test for invalid get column + */ + public function test_get_column_invalid(): void { + $entity = (new base_test_entity())->initialise(); + + $this->expectException(coding_exception::class); + $this->expectExceptionMessage('Coding error detected, it must be fixed by a programmer: ' . + 'Invalid column name (nonexistingcolumn)'); + $entity->get_column('nonexistingcolumn'); + } + + /** + * Test getting columns + */ + public function test_get_columns(): void { + $entity = (new base_test_entity())->initialise(); + + $columns = $entity->get_columns(); + $this->assertCount(1, $columns); + $this->assertContainsOnlyInstancesOf(column::class, $columns); + } + + /** + * Test getting filter + */ + public function test_get_filter(): void { + $entity = (new base_test_entity())->initialise(); + + $filter = $entity->get_filter('test'); + $this->assertEquals('base_test_entity:test', $filter->get_unique_identifier()); + } + + /** + * Test for invalid get filter + */ + public function test_get_filter_invalid(): void { + $entity = (new base_test_entity())->initialise(); + + $this->expectException(coding_exception::class); + $this->expectExceptionMessage('Coding error detected, it must be fixed by a programmer: ' . + 'Invalid filter name (nonexistingfilter)'); + $entity->get_filter('nonexistingfilter'); + } + + /** + * Test getting filters + */ + public function test_get_filters(): void { + $entity = (new base_test_entity())->initialise(); + + $filters = $entity->get_filters(); + $this->assertCount(1, $filters); + $this->assertContainsOnlyInstancesOf(filter::class, $filters); + } + + /** + * Test getting condition + */ + public function test_get_condition(): void { + $entity = (new base_test_entity())->initialise(); + + $condition = $entity->get_condition('test'); + $this->assertEquals('base_test_entity:test', $condition->get_unique_identifier()); + } + + /** + * Test for invalid get condition + */ + public function test_get_condition_invalid(): void { + $entity = (new base_test_entity())->initialise(); + + $this->expectException(coding_exception::class); + $this->expectExceptionMessage('Coding error detected, it must be fixed by a programmer: ' . + 'Invalid condition name (nonexistingcondition)'); + $entity->get_condition('nonexistingcondition'); + } + + /** + * Test getting conditions + */ + public function test_get_conditions(): void { + $entity = (new base_test_entity())->initialise(); + + $conditions = $entity->get_conditions(); + $this->assertCount(1, $conditions); + $this->assertContainsOnlyInstancesOf(filter::class, $conditions); + } +} + +/** + * Simple implementation of the base entity + */ +class base_test_entity extends base { + + /** + * Table aliases + * + * @return array + */ + protected function get_default_table_aliases(): array { + return [ + 'mytable' => 'm', + 'myothertable' => 'o', + ]; + } + + /** + * Entity title + * + * @return lang_string + */ + protected function get_default_entity_title(): lang_string { + return new lang_string('yes'); + } + + /** + * Initialise entity + * + * @return base + */ + public function initialise(): base { + $column = (new column( + 'test', + new lang_string('no'), + $this->get_entity_name() + )) + ->add_field('no'); + + $filter = (new filter( + text::class, + 'test', + new lang_string('no'), + $this->get_entity_name(), + )) + ->set_field_sql('no'); + + return $this + ->add_column($column) + ->add_filter($filter) + ->add_condition($filter); + } +} diff --git a/reportbuilder/tests/local/entities/course_test.php b/reportbuilder/tests/local/entities/course_test.php deleted file mode 100644 index 17700ca90ca..00000000000 --- a/reportbuilder/tests/local/entities/course_test.php +++ /dev/null @@ -1,515 +0,0 @@ -. - -declare(strict_types=1); - -namespace core_reportbuilder\local\entities; - -use advanced_testcase; -use coding_exception; -use context_system; -use lang_string; -use core_reportbuilder\course_entity_report; -use core_reportbuilder\manager; -use core_reportbuilder\testable_system_report_table; -use core_reportbuilder\local\filters\boolean_select; -use core_reportbuilder\local\filters\date; -use core_reportbuilder\local\filters\tags; -use core_reportbuilder\local\filters\text; -use core_reportbuilder\local\filters\select; -use core_reportbuilder\local\helpers\user_filter_manager; - -/** - * Unit tests for course entity - * - * @package core_reportbuilder - * @covers \core_reportbuilder\local\entities\base - * @covers \core_reportbuilder\local\entities\course - * @covers \core_reportbuilder\local\helpers\custom_fields - * @covers \core_reportbuilder\local\report\base - * @covers \core_reportbuilder\system_report - * @copyright 2021 David Matamoros - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class course_test extends advanced_testcase { - - /** - * Load required classes - */ - public static function setUpBeforeClass(): void { - global $CFG; - - require_once("{$CFG->dirroot}/course/lib.php"); - require_once("{$CFG->dirroot}/reportbuilder/tests/fixtures/testable_system_report_table.php"); - require_once("{$CFG->dirroot}/reportbuilder/tests/fixtures/course_entity_report.php"); - } - - /** - * Generate courses for the tests - */ - public function generate_courses(): array { - $coursecategory1 = $this->getDataGenerator()->create_category(); - $course1 = $this->getDataGenerator()->create_course([ - 'fullname' => 'Course 1', - 'shortname' => 'C1', - 'idnumber' => 'IDNumber1', - 'visible' => 1, - 'startdate' => 289308600, - 'enddate' => 3445023600, - 'category' => $coursecategory1->id, - 'groupmode' => NOGROUPS, - 'enablecompletion' => 1, - 'downloadcontent' => DOWNLOAD_COURSE_CONTENT_DISABLED, - 'format' => 'topics', - 'calendartype' => 'Gregorian', - 'theme' => 'afterburner', - 'lang' => 'en', - 'tags' => ['dancing'], - ]); - - $coursecategory2 = $this->getDataGenerator()->create_category(); - $course2 = $this->getDataGenerator()->create_course([ - 'fullname' => 'Course 2', - 'shortname' => 'C2', - 'idnumber' => 'IDNumber2', - 'visible' => 0, - 'startdate' => 1614726000, - 'enddate' => 1961881200, - 'category' => $coursecategory2->id, - 'groupmode' => SEPARATEGROUPS, - 'enablecompletion' => 0, - 'downloadcontent' => DOWNLOAD_COURSE_CONTENT_ENABLED, - 'format' => 'topics', - 'calendartype' => 'Gregorian', - 'theme' => 'afterburner', - 'lang' => 'es', - ]); - - return [$coursecategory1, $course1, $coursecategory2, $course2]; - } - - /** - * Test callbacks are correctly applied for those columns using them - */ - public function test_get_columns_with_callbacks(): void { - $this->resetAfterTest(); - - [$coursecategory1, $course1] = $this->generate_courses(); - $testdate = time(); - - // Add some customfields to the course. - $cfgenerator = self::getDataGenerator()->get_plugin_generator('core_customfield'); - $params = [ - 'component' => 'core_course', - 'area' => 'course', - 'itemid' => 0, - 'contextid' => context_system::instance()->id - ]; - $category = $cfgenerator->create_category($params); - $field1 = $cfgenerator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'text', 'name' => 'Customfield text 1', 'shortname' => 'cf1']); - $cfgenerator->add_instance_data($field1, (int)$course1->id, 'Do. Or do not. There is no try'); - $field2 = $cfgenerator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'text', 'name' => 'Customfield text 2', 'shortname' => 'cf2']); - $cfgenerator->add_instance_data($field2, (int)$course1->id, 'Chewie, we are home'); - $field3 = $cfgenerator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'checkbox', 'name' => 'Customfield checkbox', 'shortname' => 'cf3']); - $cfgenerator->add_instance_data($field3, (int)$course1->id, 1); - $field4 = $cfgenerator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'date', 'name' => 'Customfield date', 'shortname' => 'cf4']); - $cfgenerator->add_instance_data($field4, (int)$course1->id, $testdate); - $field5 = $cfgenerator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'select', 'name' => 'Customfield menu', 'shortname' => 'cf5', - 'configdata' => ['defaultvalue' => 'Option A', 'options' => "Option A\nOption B\nOption C"]]); - // Select option C for course1 (options are counted starting from one). - $cfgenerator->add_instance_data($field5, (int)$course1->id, 3); - - $tablerows = $this->get_report_table_rows(); - $courserows = array_filter($tablerows, static function(array $row) use ($course1): bool { - return $row['shortname'] === $course1->shortname; - }); - $courserow = reset($courserows); - - $this->assertEquals('Course 1', $courserow['fullname']); - $this->assertEquals('C1', $courserow['shortname']); - $this->assertEquals('IDNumber1', $courserow['idnumber']); - $this->assertEquals('Yes', $courserow['visible']); - $this->assertEquals(userdate(289308600), $courserow['startdate']); - $this->assertEquals(userdate(3445023600), $courserow['enddate']); - $this->assertEquals('No groups', $courserow['groupmode']); - $this->assertEquals('Yes', $courserow['enablecompletion']); - $this->assertEquals('No', $courserow['downloadcontent']); - $this->assertEquals('Topics format', $courserow['format']); - $this->assertEquals('Gregorian', $courserow['calendartype']); - $this->assertEquals('afterburner', $courserow['theme']); - $this->assertEquals(get_string_manager()->get_list_of_translations()['en'], $courserow['lang']); - $expected = 'Course 1'; - $this->assertEquals($expected, $courserow['coursefullnamewithlink']); - $expected = 'C1'; - $this->assertEquals($expected, $courserow['courseshortnamewithlink']); - $expected = 'IDNumber1'; - $this->assertEquals($expected, $courserow['courseidnumberewithlink']); - $this->assertEquals('Do. Or do not. There is no try', $courserow['customfield_cf1']); - $this->assertEquals('Chewie, we are home', $courserow['customfield_cf2']); - $this->assertEquals('Yes', $courserow['customfield_cf3']); - $this->assertEquals(userdate($testdate), $courserow['customfield_cf4']); - $this->assertEquals('Option C', $courserow['customfield_cf5']); - } - - /** - * Test filtering report by course fields - */ - public function test_filters(): void { - global $DB; - - $this->resetAfterTest(); - - [$coursecategory1] = $this->generate_courses(); - - // Filter by fullname field. - $filtervalues = [ - 'course:fullname_operator' => text::IS_EQUAL_TO, - 'course:fullname_value' => 'Course 1', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Course 1', - ], array_column($tablerows, 'fullname')); - - // Filter by shortname field. - $filtervalues = [ - 'course:shortname_operator' => text::IS_EQUAL_TO, - 'course:shortname_value' => 'C1', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Course 1', - ], array_column($tablerows, 'fullname')); - - // Filter by idnumber field. - $filtervalues = [ - 'course:idnumber_operator' => text::IS_EQUAL_TO, - 'course:idnumber_value' => 'IDNumber2', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Course 2', - ], array_column($tablerows, 'fullname')); - - // Filter by visible field. - $filtervalues = [ - 'course:visible_operator' => boolean_select::NOT_CHECKED, - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Course 2', - ], array_column($tablerows, 'fullname')); - - // Filter by startdate field. - $filtervalues = [ - 'course:startdate_operator' => date::DATE_RANGE, - 'course:startdate_from' => 289135800, - 'course:startdate_to' => 289740600, - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Course 1', - ], array_column($tablerows, 'fullname')); - - // Filter by group mode field. - $filtervalues = [ - 'course:groupmode_operator' => select::EQUAL_TO, - 'course:groupmode_value' => SEPARATEGROUPS, - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Course 2', - ], array_column($tablerows, 'fullname')); - - // Filter by enable completion field. - $filtervalues = [ - 'course:enablecompletion_operator' => boolean_select::CHECKED, - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Course 1', - ], array_column($tablerows, 'fullname')); - } - - /** - * Test filtering report by course customfield - */ - public function test_customfield_text_filter(): void { - $this->resetAfterTest(); - - $course1 = $this->getDataGenerator()->create_course([ - 'fullname' => 'Philosophy and Superheroes', - 'shortname' => 'PS1', - ]); - $course2 = $this->getDataGenerator()->create_course([ - 'fullname' => 'The game of Mathematics', - 'shortname' => 'GM1', - ]); - - // Add some customfields to the course. - $cfgenerator = self::getDataGenerator()->get_plugin_generator('core_customfield'); - $params = [ - 'component' => 'core_course', - 'area' => 'course', - 'itemid' => 0, - 'contextid' => context_system::instance()->id - ]; - $category = $cfgenerator->create_category($params); - $field = $cfgenerator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'text', 'name' => 'Customfield text 1', 'shortname' => 'cf']); - $cfgenerator->add_instance_data($field, (int)$course1->id, 'Leia Organa'); - $cfgenerator->add_instance_data($field, (int)$course2->id, 'Han Solo'); - $field5 = $cfgenerator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'select', 'name' => 'Customfield menu', 'shortname' => 'cf5', - 'configdata' => ['defaultvalue' => 'Option A', 'options' => "Option A\nOption B\nOption C"]]); - $cfgenerator->add_instance_data($field5, (int)$course1->id, 3); - $cfgenerator->add_instance_data($field5, (int)$course2->id, 2); - - $filtervalues = [ - 'course:customfield_cf_operator' => text::IS_EQUAL_TO, - 'course:customfield_cf_value' => 'Leia Organa', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Philosophy and Superheroes', - ], array_column($tablerows, 'fullname')); - - $filtervalues = [ - 'course:customfield_cf_operator' => text::IS_EQUAL_TO, - 'course:customfield_cf_value' => 'Han Solo', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'The game of Mathematics', - ], array_column($tablerows, 'fullname')); - - // Filter by menu customfield. - $filtervalues = [ - 'course:customfield_cf5_operator' => select::EQUAL_TO, - 'course:customfield_cf5_value' => 3, // Option C. - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Philosophy and Superheroes', - ], array_column($tablerows, 'fullname')); - - // Filter by course customfield that doesn't exist. - $filtervalues = [ - 'course:customfield_cf_operator' => text::IS_EQUAL_TO, - 'course:customfield_cf_value' => 'Luke Skywalker', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEmpty($tablerows); - } - - /** - * Helper method to create the report, and return it's rows - * - * @param array $filtervalues - * @return array - */ - private function get_report_table_rows(array $filtervalues = []): array { - $report = manager::create_report_persistent((object) [ - 'type' => course_entity_report::TYPE_SYSTEM_REPORT, - 'source' => course_entity_report::class, - ]); - - user_filter_manager::set($report->get('id'), $filtervalues); - - return testable_system_report_table::create($report->get('id'), [])->get_table_rows(); - } - - /** - * Test entity table alias - */ - public function test_get_table_alias(): void { - $courseentity = new course(); - $this->assertEquals('c', $courseentity->get_table_alias('course')); - } - - /** - * Test for invalid get table alias - */ - public function test_get_table_alias_invalid(): void { - $courseentity = new course(); - - $this->expectException(coding_exception::class); - $this->expectExceptionMessage('Coding error detected, it must be fixed by a programmer: ' . - 'Invalid table name (nonexistingalias)'); - $courseentity->get_table_alias('nonexistingalias'); - } - - /** - * Test setting table alias - */ - public function test_set_table_alias(): void { - $courseentity = new course(); - - $courseentity->set_table_alias('course', 'newalias'); - $this->assertEquals('newalias', $courseentity->get_table_alias('course')); - } - - /** - * Test invalid entity set table alias - */ - public function test_set_table_alias_invalid(): void { - $courseentity = new course(); - - $this->expectException(coding_exception::class); - $this->expectExceptionMessage('Coding error detected, it must be fixed by a programmer: Invalid table name (nonexistent)'); - $courseentity->set_table_alias('nonexistent', 'newalias'); - } - - /** - * Test setting multiple table aliases - */ - public function test_set_table_aliases(): void { - $courseentity = new course(); - - $courseentity->set_table_aliases([ - 'course' => 'newalias', - 'context' => 'newalias2', - ]); - $this->assertEquals('newalias', $courseentity->get_table_alias('course')); - $this->assertEquals('newalias2', $courseentity->get_table_alias('context')); - } - - /** - * Test setting multiple table aliases, containing an invalid table - */ - public function test_set_table_aliases_invalid(): void { - $courseentity = new course(); - - $this->expectException(coding_exception::class); - $this->expectExceptionMessage('Coding error detected, it must be fixed by a programmer: Invalid table name (nonexistent)'); - $courseentity->set_table_aliases([ - 'course' => 'newalias', - 'nonexistent' => 'newalias2', - ]); - } - - /** - * Test entity name - */ - public function test_name(): void { - $courseentity = new course(); - - $this->assertEquals('course', $courseentity->get_entity_name()); - - $courseentity->set_entity_name('newentityname'); - $this->assertEquals('newentityname', $courseentity->get_entity_name()); - } - - /** - * Test invalid entity name - */ - public function test_name_invalid(): void { - $courseentity = new course(); - - $this->expectException(coding_exception::class); - $this->expectExceptionMessage('Entity name must be comprised of alphanumeric character, underscore or dash'); - $courseentity->set_entity_name(''); - } - - /** - * Test entity title - */ - public function test_title(): void { - $courseentity = new course(); - - $this->assertEquals(new lang_string('entitycourse', 'core_reportbuilder'), $courseentity->get_entity_title()); - - $newtitle = new lang_string('fullname'); - $courseentity->set_entity_title($newtitle); - $this->assertEquals($newtitle, $courseentity->get_entity_title()); - } - - /** - * Test adding single join - */ - public function test_add_join(): void { - $courseentity = (new course()) - ->set_table_alias('course', 'c1'); - - $tablejoin = "JOIN {course} c2 ON c2.id = c1.id"; - $courseentity->add_join($tablejoin); - - $this->assertEquals([$tablejoin], $courseentity->get_joins()); - } - - /** - * Test adding multiple joins - */ - public function test_add_joins(): void { - $courseentity = (new course()) - ->set_table_alias('course', 'c1'); - - $tablejoins = [ - "JOIN {course} c2 ON c2.id = c1.id", - "JOIN {course} c3 ON c3.id = c1.id", - ]; - $courseentity->add_joins($tablejoins); - - $this->assertEquals($tablejoins, $courseentity->get_joins()); - } - - /** - * Test adding duplicate joins - */ - public function test_add_duplicate_joins(): void { - $courseentity = (new course()) - ->set_table_alias('course', 'c1'); - - $tablejoins = [ - "JOIN {course} c2 ON c2.id = c1.id", - "JOIN {course} c3 ON c3.id = c1.id", - ]; - $courseentity - ->add_joins($tablejoins) - ->add_joins($tablejoins); - - $this->assertEquals($tablejoins, $courseentity->get_joins()); - } - - /** - * Test for invalid get column - */ - public function test_get_column_invalid(): void { - $courseentity = new course(); - - $this->expectException(coding_exception::class); - $this->expectExceptionMessage('Coding error detected, it must be fixed by a programmer: ' . - 'Invalid column name (nonexistingcolumn)'); - $courseentity->get_column('nonexistingcolumn'); - } - - /** - * Test for invalid get filter - */ - public function test_get_filter_invalid(): void { - $courseentity = new course(); - - $this->expectException(coding_exception::class); - $this->expectExceptionMessage('Coding error detected, it must be fixed by a programmer: ' . - 'Invalid filter name (nonexistingfilter)'); - $courseentity->get_filter('nonexistingfilter'); - } -} diff --git a/reportbuilder/tests/local/entities/user_test.php b/reportbuilder/tests/local/entities/user_test.php index ae45d358778..c6a10beb5e3 100644 --- a/reportbuilder/tests/local/entities/user_test.php +++ b/reportbuilder/tests/local/entities/user_test.php @@ -19,295 +19,51 @@ declare(strict_types=1); namespace core_reportbuilder\local\entities; use advanced_testcase; -use moodle_url; -use core_reportbuilder\manager; -use core_reportbuilder\testable_system_report_table; -use core_reportbuilder\user_entity_report; -use core_reportbuilder\local\filters\boolean_select; -use core_reportbuilder\local\filters\date; -use core_reportbuilder\local\filters\select; -use core_reportbuilder\local\filters\tags; -use core_reportbuilder\local\filters\text; -use core_reportbuilder\local\helpers\user_filter_manager; /** * Unit tests for user entity * * @package core_reportbuilder - * @covers \core_reportbuilder\local\entities\base * @covers \core_reportbuilder\local\entities\user - * @covers \core_reportbuilder\local\helpers\user_profile_fields - * @covers \core_reportbuilder\local\report\base - * @covers \core_reportbuilder\system_report * @copyright 2021 Paul Holden * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class user_test extends advanced_testcase { /** - * Load required classes + * Test getting user identity column */ - public static function setUpBeforeClass(): void { - global $CFG; - - require_once("{$CFG->dirroot}/reportbuilder/tests/fixtures/testable_system_report_table.php"); - require_once("{$CFG->dirroot}/reportbuilder/tests/fixtures/user_entity_report.php"); - require_once("{$CFG->dirroot}/user/profile/lib.php"); - } - - /** - * Test callbacks are correctly applied for those columns using them - */ - public function test_columns_with_callbacks(): void { + public function test_get_identity_column(): void { $this->resetAfterTest(); - // Add a couple of user profile fields to show on the report. - $this->getDataGenerator()->create_custom_profile_field(['datatype' => 'text', - 'shortname' => 'favcolor', 'name' => 'Favorite color']); - $this->getDataGenerator()->create_custom_profile_field(['datatype' => 'text', - 'shortname' => 'favsuperpower', 'name' => 'Favorite super power']); + $this->getDataGenerator()->create_custom_profile_field(['datatype' => 'text', 'name' => 'Hi', 'shortname' => 'hi']); - $user = $this->getDataGenerator()->create_user([ - 'suspended' => 1, - 'confirmed' => 0, - 'country' => 'ES', - 'interests' => ['dancing'], - 'profile_field_favcolor' => 'Blue', - 'profile_field_favsuperpower' => 'Time travel', - ]); + $user = new user(); + $user->initialise(); - $tablerows = $this->get_report_table_rows(); - $userrows = array_filter($tablerows, static function(array $row) use ($user): bool { - return $row['username'] === $user->username; - }); - $userrow = reset($userrows); + $columnusername = $user->get_identity_column('username'); + $this->assertEquals('user:username', $columnusername->get_unique_identifier()); - $this->assertEquals('Yes', $userrow['suspended']); - $this->assertEquals('No', $userrow['confirmed']); - $this->assertEquals('Spain', $userrow['country']); - $this->assertEquals('Blue', $userrow['profilefield_favcolor']); - $this->assertEquals('Time travel', $userrow['profilefield_favsuperpower']); + $columnprofilefield = $user->get_identity_column('profile_field_hi'); + $this->assertEquals('user:profilefield_hi', $columnprofilefield->get_unique_identifier()); } /** - * Test the formatted user fullname columns + * Test getting user identity filter */ - public function test_fullname_columns(): void { - global $OUTPUT; - + public function test_get_identity_filter(): void { $this->resetAfterTest(); - $user = $this->getDataGenerator()->create_user([]); + $this->getDataGenerator()->create_custom_profile_field(['datatype' => 'text', 'name' => 'Hi', 'shortname' => 'hi']); - $tablerows = $this->get_report_table_rows(); - $userrows = array_filter($tablerows, static function(array $row) use ($user): bool { - return $row['username'] === $user->username; - }); - $userrow = reset($userrows); + $user = new user(); + $user->initialise(); - $userfullname = fullname($user); - $userprofile = (new moodle_url('/user/profile.php', ['id' => $user->id]))->out(); - $userpicture = $OUTPUT->user_picture($user, ['link' => false, 'alttext' => false]); + $filterusername = $user->get_identity_filter('username'); + $this->assertEquals('user:username', $filterusername->get_unique_identifier()); - $this->assertEquals($userfullname, $userrow['fullname']); - $this->assertEquals('' . $userfullname . '', $userrow['fullnamewithlink']); - $this->assertEquals($userpicture . $userfullname, $userrow['fullnamewithpicture']); - $this->assertEquals('' . $userpicture . $userfullname . '', - $userrow['fullnamewithpicturelink']); - } - - /** - * Test picture column callback - */ - public function test_picture_column(): void { - global $OUTPUT; - - $this->resetAfterTest(); - - $user = $this->getDataGenerator()->create_user([]); - - $tablerows = $this->get_report_table_rows(); - $userrows = array_filter($tablerows, static function(array $row) use ($user): bool { - return $row['username'] === $user->username; - }); - $userrow = reset($userrows); - - $userpicture = $OUTPUT->user_picture($user, ['link' => false, 'alttext' => false]); - $this->assertEquals($userpicture, $userrow['picture']); - } - - /** - * Test filtering report by user fields - */ - public function test_filters(): void { - global $DB; - - $this->resetAfterTest(); - - $this->getDataGenerator()->create_user(['firstname' => 'Daffy', 'lastname' => 'Duck', 'email' => 'daffy@test.com', - 'city' => 'LA', 'lastaccess' => time() - YEARSECS, 'suspended' => 1, 'interests' => ['dancing']]); - $this->getDataGenerator()->create_user(['firstname' => 'Donald', 'lastname' => 'Duck', 'email' => 'donald@test.com', - 'city' => 'Chicago', 'lastaccess' => time(), 'suspended' => 0]); - - // Filter by fullname field. - $filtervalues = [ - 'user:fullname_operator' => text::IS_EQUAL_TO, - 'user:fullname_value' => 'Daffy Duck', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Daffy Duck', - ], array_column($tablerows, 'fullname')); - - // Filter by firstname field. - $filtervalues = [ - 'user:firstname_operator' => text::CONTAINS, - 'user:firstname_value' => 'Donald', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Donald Duck', - ], array_column($tablerows, 'fullname')); - - // Filter by lastname field. - $filtervalues = [ - 'user:lastname_operator' => text::CONTAINS, - 'user:lastname_value' => 'Duck', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEqualsCanonicalizing([ - 'Donald Duck', - 'Daffy Duck', - ], array_column($tablerows, 'fullname')); - - // Filter by email field. - $filtervalues = [ - 'user:email_operator' => text::IS_EQUAL_TO, - 'user:email_value' => 'donald@test.com', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Donald Duck', - ], array_column($tablerows, 'fullname')); - - // Filter by city field. - $filtervalues = [ - 'user:city_operator' => text::IS_EQUAL_TO, - 'user:city_value' => 'Chicago', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Donald Duck', - ], array_column($tablerows, 'fullname')); - - // Filter by city field. - $filtervalues = [ - 'user:city_operator' => text::IS_EQUAL_TO, - 'user:city_value' => 'Chicago', - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Donald Duck', - ], array_column($tablerows, 'fullname')); - - // Filter by lastaccess field. - $filtervalues = [ - 'user:lastaccess_operator' => date::DATE_RANGE, - 'user:lastaccess_from' => time() - YEARSECS - 100, - 'user:lastaccess_to' => time() - YEARSECS + 100, - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Daffy Duck', - ], array_column($tablerows, 'fullname')); - - // Filter by suspened field. - $filtervalues = [ - 'user:suspended_operator' => boolean_select::CHECKED, - ]; - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Daffy Duck', - ], array_column($tablerows, 'fullname')); - } - - /** - * Test filtering report by a user profile field - */ - public function test_userprofilefield_filter(): void { - $this->resetAfterTest(); - - // Add a user profile field to show on the report. - $this->getDataGenerator()->create_custom_profile_field(['datatype' => 'text', - 'shortname' => 'favcolor', 'name' => 'Favorite color']); - - $this->getDataGenerator()->create_user(['firstname' => 'Daffy', 'lastname' => 'Duck', 'profile_field_favcolor' => 'Blue']); - $this->getDataGenerator()->create_user(['firstname' => 'Donald', 'lastname' => 'Duck', - 'profile_field_favcolor' => 'Green']); - - $filtervalues = [ - 'user:profilefield_favcolor_operator' => text::IS_EQUAL_TO, - 'user:profilefield_favcolor_value' => 'Green', - ]; - - $tablerows = $this->get_report_table_rows($filtervalues); - $this->assertEquals([ - 'Donald Duck', - ], array_column($tablerows, 'fullname')); - } - - /** - * Data provider for {@see test_userprofilefield_filter_empty} - * - * @return array - */ - public function userprofilefield_filter_empty_provider(): array { - return [ - ['checkbox', 1, boolean_select::NOT_CHECKED], - ['text', 'Hello', text::IS_EMPTY], - ['text', 'Hello', text::IS_NOT_EQUAL_TO], - ['text', 'Hello', text::DOES_NOT_CONTAIN], - ['menu', 'one', select::NOT_EQUAL_TO, "one\ntwo"], - ]; - } - - /** - * Test filtering report by a user profile field with negated operators (contains the "empty" value appropriate to the field - * type, or is not set/null) - * - * @param string $datatype - * @param mixed $userfieldvalue - * @param int $operator - * @param string $datatypeparam1 - * - * @dataProvider userprofilefield_filter_empty_provider - */ - public function test_userprofilefield_filter_empty(string $datatype, $userfieldvalue, int $operator, - string $datatypeparam1 = ''): void { - - $this->resetAfterTest(); - - $this->getDataGenerator()->create_custom_profile_field([ - 'datatype' => $datatype, - 'shortname' => 'test', - 'name' => 'My test field', - 'param1' => $datatypeparam1, - ]); - - // At this point, the custom profile field was created after the admin account, so the value will be not set/null. - $filtervalues = [ - 'user:profilefield_test_operator' => $operator, - 'user:profilefield_test_value' => $userfieldvalue, - ]; - - // Create a user who does have the field set. - $user = $this->getDataGenerator()->create_user(['profile_field_test' => $userfieldvalue]); - - $rows = $this->get_report_table_rows($filtervalues); - - $usernames = array_column($rows, 'username'); - $this->assertContains('admin', $usernames); - $this->assertNotContains($user->username, $usernames); + $filterprofilefield = $user->get_identity_filter('profile_field_hi'); + $this->assertEquals('user:profilefield_hi', $filterprofilefield->get_unique_identifier()); } /** @@ -344,21 +100,4 @@ class user_test extends advanced_testcase { // Ensure we received back all name fields. $this->assertEquals($expecteduserfields, array_keys((array) $user)); } - - /** - * Helper method to create the report, and return it's rows - * - * @param array $filtervalues - * @return array - */ - private function get_report_table_rows(array $filtervalues = []): array { - $report = manager::create_report_persistent((object) [ - 'type' => user_entity_report::TYPE_SYSTEM_REPORT, - 'source' => user_entity_report::class, - ]); - - user_filter_manager::set($report->get('id'), $filtervalues); - - return testable_system_report_table::create($report->get('id'), [])->get_table_rows(); - } } diff --git a/reportbuilder/tests/local/helpers/custom_fields_test.php b/reportbuilder/tests/local/helpers/custom_fields_test.php index 421afdd8f25..d4c4f904b96 100644 --- a/reportbuilder/tests/local/helpers/custom_fields_test.php +++ b/reportbuilder/tests/local/helpers/custom_fields_test.php @@ -18,10 +18,22 @@ declare(strict_types=1); namespace core_reportbuilder\local\helpers; -use advanced_testcase; +use core_customfield_generator; +use core_reportbuilder_generator; +use core_reportbuilder_testcase; use core_reportbuilder\local\entities\course; +use core_reportbuilder\local\filters\boolean_select; +use core_reportbuilder\local\filters\date; +use core_reportbuilder\local\filters\select; +use core_reportbuilder\local\filters\text; use core_reportbuilder\local\report\column; use core_reportbuilder\local\report\filter; +use core_course\reportbuilder\datasource\courses; + +defined('MOODLE_INTERNAL') || die(); + +global $CFG; +require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php"); /** * Unit tests for custom fields helper @@ -31,29 +43,36 @@ use core_reportbuilder\local\report\filter; * @copyright 2021 David Matamoros * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class custom_fields_test extends advanced_testcase { +class custom_fields_test extends core_reportbuilder_testcase { /** - * Generate a course with customfields + * Generate custom fields, one of each type + * + * @return custom_fields */ - public function generate_course_with_customfields(): custom_fields { - $course = $this->getDataGenerator()->create_course(); + private function generate_customfields(): custom_fields { - // Add some customfields to the course. - $cfgenerator = self::getDataGenerator()->get_plugin_generator('core_customfield'); - $params = [ + /** @var core_customfield_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_customfield'); + $category = $generator->create_category([ 'component' => 'core_course', 'area' => 'course', 'itemid' => 0, 'contextid' => \context_system::instance()->id - ]; - $category = $cfgenerator->create_category($params); - $field1 = $cfgenerator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'text', 'name' => 'Customfield text 1', 'shortname' => 'cf1']); - $cfgenerator->add_instance_data($field1, (int)$course->id, 'C-3PO'); - $field2 = $cfgenerator->create_field( - ['categoryid' => $category->get('id'), 'type' => 'text', 'name' => 'Customfield text 2', 'shortname' => 'cf2']); - $cfgenerator->add_instance_data($field2, (int)$course->id, 'R2-D2'); + ]); + + $generator->create_field( + ['categoryid' => $category->get('id'), 'type' => 'text', 'name' => 'Text', 'shortname' => 'text']); + + $generator->create_field( + ['categoryid' => $category->get('id'), 'type' => 'checkbox', 'name' => 'Checkbox', 'shortname' => 'checkbox']); + + $generator->create_field( + ['categoryid' => $category->get('id'), 'type' => 'date', 'name' => 'Date', 'shortname' => 'date']); + + $generator->create_field( + ['categoryid' => $category->get('id'), 'type' => 'select', 'name' => 'Select', 'shortname' => 'select', + 'configdata' => ['options' => "Cat\nDog", 'defaultvalue' => 'Cat']]); $courseentity = new course(); $coursealias = $courseentity->get_table_alias('course'); @@ -69,14 +88,16 @@ class custom_fields_test extends advanced_testcase { public function test_get_columns(): void { $this->resetAfterTest(); - $customfields = $this->generate_course_with_customfields(); + $customfields = $this->generate_customfields(); $columns = $customfields->get_columns(); - $this->assertCount(2, $columns); - [$column0, $column1] = $columns; - $this->assertInstanceOf(column::class, $column0); - $this->assertInstanceOf(column::class, $column1); - $this->assertEqualsCanonicalizing(['Customfield text 1', 'Customfield text 2'], - [$column0->get_title(), $column1->get_title()]); + + $this->assertCount(4, $columns); + $this->assertContainsOnlyInstancesOf(column::class, $columns); + + [$column0, $column1, $column2, $column3] = $columns; + $this->assertEqualsCanonicalizing(['Text', 'Checkbox', 'Date', 'Select'], + [$column0->get_title(), $column1->get_title(), $column2->get_title(), $column3->get_title()]); + $this->assertEquals(column::TYPE_TEXT, $column0->get_type()); $this->assertEquals('course', $column0->get_entity_name()); $this->assertStringStartsWith('LEFT JOIN {customfield_data}', $column0->get_joins()[0]); @@ -90,7 +111,7 @@ class custom_fields_test extends advanced_testcase { public function test_add_join(): void { $this->resetAfterTest(); - $customfields = $this->generate_course_with_customfields(); + $customfields = $this->generate_customfields(); $columns = $customfields->get_columns(); $this->assertCount(1, ($columns[0])->get_joins()); @@ -105,7 +126,7 @@ class custom_fields_test extends advanced_testcase { public function test_add_joins(): void { $this->resetAfterTest(); - $customfields = $this->generate_course_with_customfields(); + $customfields = $this->generate_customfields(); $columns = $customfields->get_columns(); $this->assertCount(1, ($columns[0])->get_joins()); @@ -120,14 +141,132 @@ class custom_fields_test extends advanced_testcase { public function test_get_filters(): void { $this->resetAfterTest(); - $customfields = $this->generate_course_with_customfields(); + $customfields = $this->generate_customfields(); $filters = $customfields->get_filters(); - $this->assertCount(2, $filters); - [$filter0, $filter1] = $filters; - $this->assertInstanceOf(filter::class, $filter0); - $this->assertInstanceOf(filter::class, $filter1); - $this->assertEqualsCanonicalizing(['Customfield text 1', 'Customfield text 2'], - [$filter0->get_header(), $filter1->get_header()]); + + $this->assertCount(4, $filters); + $this->assertContainsOnlyInstancesOf(filter::class, $filters); + + [$filter0, $filter1, $filter2, $filter3] = $filters; + $this->assertEqualsCanonicalizing(['Text', 'Checkbox', 'Date', 'Select'], + [$filter0->get_header(), $filter1->get_header(), $filter2->get_header(), $filter3->get_header()]); + } + + /** + * Test that adding custom field columns to a report returns expected values + */ + public function test_custom_report_content(): void { + $this->resetAfterTest(); + + $this->generate_customfields(); + + $course = $this->getDataGenerator()->create_course(['customfields' => [ + ['shortname' => 'text', 'value' => 'Hello'], + ['shortname' => 'checkbox', 'value' => true], + ['shortname' => 'date', 'value' => 1669852800], + ['shortname' => 'select', 'value' => 2], + ]]); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + $report = $generator->create_report(['name' => 'Courses', 'source' => courses::class, 'default' => 0]); + + // Add user profile field columns to the report. + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_text']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_checkbox']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_date']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_select']); + + $content = $this->get_custom_report_content($report->get('id')); + + $this->assertEquals([ + $course->fullname, + 'Hello', + 'Yes', + userdate(1669852800), + 'Dog' + ], array_values($content[0])); + } + + /** + * Data provider for {@see test_custom_report_filter} + * + * @return array[] + */ + public function custom_report_filter_provider(): array { + return [ + 'Filter by text custom field' => ['course:customfield_text', [ + 'course:customfield_text_operator' => text::IS_EQUAL_TO, + 'course:customfield_text_value' => 'Hello', + ], true], + 'Filter by text custom field (no match)' => ['course:customfield_text', [ + 'course:customfield_text_operator' => text::IS_EQUAL_TO, + 'course:customfield_text_value' => 'Goodbye', + ], false], + 'Filter by checkbox custom field' => ['course:customfield_checkbox', [ + 'course:customfield_checkbox_operator' => boolean_select::CHECKED, + ], true], + 'Filter by checkbox custom field (no match)' => ['course:customfield_checkbox', [ + 'course:customfield_checkbox_operator' => boolean_select::NOT_CHECKED, + ], false], + 'Filter by date custom field' => ['course:customfield_date', [ + 'course:customfield_date_operator' => date::DATE_RANGE, + 'course:customfield_date_from' => 1622502000, + ], true], + 'Filter by date custom field (no match)' => ['course:customfield_date', [ + 'course:customfield_date_operator' => date::DATE_RANGE, + 'course:customfield_date_to' => 1622502000, + ], false], + 'Filter by select custom field' => ['course:customfield_select', [ + 'course:customfield_select_operator' => select::EQUAL_TO, + 'course:customfield_select_value' => 2, + ], true], + 'Filter by select custom field (no match)' => ['course:customfield_select', [ + 'course:customfield_select_operator' => select::EQUAL_TO, + 'course:customfield_select_value' => 1, + ], false], + ]; + } + + /** + * Test filtering report by custom fields + * + * @param string $filtername + * @param array $filtervalues + * @param bool $expectmatch + * + * @dataProvider custom_report_filter_provider + */ + public function test_custom_report_filter(string $filtername, array $filtervalues, bool $expectmatch): void { + $this->resetAfterTest(); + + $this->generate_customfields(); + + $course = $this->getDataGenerator()->create_course(['customfields' => [ + ['shortname' => 'text', 'value' => 'Hello'], + ['shortname' => 'checkbox', 'value' => true], + ['shortname' => 'date', 'value' => 1669852800], + ['shortname' => 'select', 'value' => 2], + ]]); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + // Create report containing single column, and given filter. + $report = $generator->create_report(['name' => 'Users', 'source' => courses::class, 'default' => 0]); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname']); + + // Add filter, set it's values. + $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]); + $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues); + + if ($expectmatch) { + $this->assertCount(1, $content); + $this->assertEquals($course->fullname, reset($content[0])); + } else { + $this->assertEmpty($content); + } } } diff --git a/reportbuilder/tests/local/helpers/user_profile_fields_test.php b/reportbuilder/tests/local/helpers/user_profile_fields_test.php index 5b7d1480ce4..5b3a7452a5d 100644 --- a/reportbuilder/tests/local/helpers/user_profile_fields_test.php +++ b/reportbuilder/tests/local/helpers/user_profile_fields_test.php @@ -21,6 +21,10 @@ namespace core_reportbuilder\local\helpers; use core_reportbuilder_generator; use core_reportbuilder_testcase; use core_reportbuilder\local\entities\user; +use core_reportbuilder\local\filters\boolean_select; +use core_reportbuilder\local\filters\date; +use core_reportbuilder\local\filters\select; +use core_reportbuilder\local\filters\text; use core_reportbuilder\local\report\column; use core_reportbuilder\local\report\filter; use core_user\reportbuilder\datasource\users; @@ -223,4 +227,98 @@ class user_profile_fields_test extends core_reportbuilder_testcase { ], ], $content); } + + /** + * Data provider for {@see test_custom_report_filter} + * + * @return array[] + */ + public function custom_report_filter_provider(): array { + return [ + 'Filter by checkbox profile field' => ['user:profilefield_checkbox', [ + 'user:profilefield_checkbox_operator' => boolean_select::CHECKED, + ], 'testuser'], + 'Filter by checkbox profile field (empty)' => ['user:profilefield_checkbox', [ + 'user:profilefield_checkbox_operator' => boolean_select::NOT_CHECKED, + ], 'admin'], + 'Filter by datetime profile field' => ['user:profilefield_datetime', [ + 'user:profilefield_datetime_operator' => date::DATE_RANGE, + 'user:profilefield_datetime_from' => 1622502000, + ], 'testuser'], + 'Filter by datetime profile field (empty)' => ['user:profilefield_datetime', [ + 'user:profilefield_datetime_operator' => date::DATE_EMPTY, + ], 'admin'], + 'Filter by menu profile field' => ['user:profilefield_menu', [ + 'user:profilefield_menu_operator' => select::EQUAL_TO, + 'user:profilefield_menu_value' => 'Dog', + ], 'testuser'], + 'Filter by menu profile field (empty)' => ['user:profilefield_menu', [ + 'user:profilefield_menu_operator' => select::NOT_EQUAL_TO, + 'user:profilefield_menu_value' => 'Dog', + ], 'admin'], + 'Filter by social profile field' => ['user:profilefield_social', [ + 'user:profilefield_social_operator' => text::IS_EQUAL_TO, + 'user:profilefield_social_value' => '12345', + ], 'testuser'], + 'Filter by social profile field (empty)' => ['user:profilefield_social', [ + 'user:profilefield_social_operator' => text::IS_EMPTY, + ], 'admin'], + 'Filter by text profile field' => ['user:profilefield_text', [ + 'user:profilefield_text_operator' => text::IS_EQUAL_TO, + 'user:profilefield_text_value' => 'Hello', + ], 'testuser'], + 'Filter by text profile field (empty)' => ['user:profilefield_text', [ + 'user:profilefield_text_operator' => text::IS_NOT_EQUAL_TO, + 'user:profilefield_text_value' => 'Hello', + ], 'admin'], + 'Filter by textarea profile field' => ['user:profilefield_textarea', [ + 'user:profilefield_textarea_operator' => text::IS_EQUAL_TO, + 'user:profilefield_textarea_value' => 'Goodbye', + ], 'testuser'], + 'Filter by textarea profile field (empty)' => ['user:profilefield_textarea', [ + 'user:profilefield_textarea_operator' => text::DOES_NOT_CONTAIN, + 'user:profilefield_textarea_value' => 'Goodbye', + ], 'admin'], + ]; + } + + /** + * Test filtering report by custom profile fields + * + * @param string $filtername + * @param array $filtervalues + * @param string $expectmatchuser + * + * @dataProvider custom_report_filter_provider + */ + public function test_custom_report_filter(string $filtername, array $filtervalues, string $expectmatchuser): void { + $this->resetAfterTest(); + + $userprofilefields = $this->generate_userprofilefields(); + + // Create test subject with user profile fields content. + $user = $this->getDataGenerator()->create_user([ + 'username' => 'testuser', + 'profile_field_checkbox' => true, + 'profile_field_datetime' => '2021-12-09', + 'profile_field_menu' => 'Dog', + 'profile_field_Social' => '12345', + 'profile_field_text' => 'Hello', + 'profile_field_textarea' => 'Goodbye', + ]); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + // Create report containing single column, and given filter. + $report = $generator->create_report(['name' => 'Users', 'source' => users::class, 'default' => 0]); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:username']); + + // Add filter, set it's values. + $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]); + $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues); + + $this->assertCount(1, $content); + $this->assertEquals($expectmatchuser, reset($content[0])); + } } diff --git a/user/tests/reportbuilder/datasource/users_test.php b/user/tests/reportbuilder/datasource/users_test.php index 363c7bda5ac..dc75418bbbe 100644 --- a/user/tests/reportbuilder/datasource/users_test.php +++ b/user/tests/reportbuilder/datasource/users_test.php @@ -21,7 +21,12 @@ namespace core_user\reportbuilder\datasource; use core_collator; use core_reportbuilder_testcase; use core_reportbuilder_generator; +use core_reportbuilder\local\filters\boolean_select; +use core_reportbuilder\local\filters\date; +use core_reportbuilder\local\filters\select; use core_reportbuilder\local\filters\tags; +use core_reportbuilder\local\filters\text; +use core_reportbuilder\local\filters\user as user_filter; defined('MOODLE_INTERNAL') || die(); @@ -68,14 +73,43 @@ class users_test extends core_reportbuilder_testcase { public function test_datasource_non_default_columns(): void { $this->resetAfterTest(); - $user = $this->getDataGenerator()->create_user(['firstname' => 'Zoe', 'interests' => ['Horses']]); + $user = $this->getDataGenerator()->create_user([ + 'firstname' => 'Zoe', + 'idnumber' => 'U0001', + 'city' => 'London', + 'country' => 'GB', + 'interests' => ['Horses'], + ]); /** @var core_reportbuilder_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); $report = $generator->create_report(['name' => 'Users', 'source' => users::class, 'default' => 0]); // User. + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullnamewithlink']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullnamewithpicture']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullnamewithpicturelink']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:picture']); $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:firstname']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:city']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:country']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:description']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:firstnamephonetic']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastnamephonetic']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:middlename']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:alternatename']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:idnumber']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:institution']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:department']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:phone1']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:phone2']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:address']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastaccess']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:suspended']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:confirmed']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:moodlenetprofile']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:timecreated']); // Tags. $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:name']); @@ -85,18 +119,44 @@ class users_test extends core_reportbuilder_testcase { $this->assertCount(2, $content); // Consistent order by firstname, just in case. - core_collator::asort_array_of_arrays_by_key($content, 'c0_firstname'); + core_collator::asort_array_of_arrays_by_key($content, 'c4_firstname'); $content = array_values($content); [$adminrow, $userrow] = array_map('array_values', $content); - $this->assertEquals('Admin', $adminrow[0]); - $this->assertEmpty($adminrow[1]); - $this->assertEmpty($adminrow[2]); + $this->assertStringContainsString('Admin User', $adminrow[0]); + $this->assertStringContainsString('Admin User', $adminrow[1]); + $this->assertStringContainsString('Admin User', $adminrow[2]); + $this->assertNotEmpty($adminrow[3]); + $this->assertEquals('Admin', $adminrow[4]); + $this->assertEquals('User', $adminrow[5]); - $this->assertEquals('Zoe', $userrow[0]); - $this->assertEquals('Horses', $userrow[1]); - $this->assertStringContainsString('Horses', $userrow[2]); + $this->assertStringContainsString(fullname($user), $userrow[0]); + $this->assertStringContainsString(fullname($user), $userrow[1]); + $this->assertStringContainsString(fullname($user), $userrow[2]); + $this->assertNotEmpty($userrow[3]); + $this->assertEquals($user->firstname, $userrow[4]); + $this->assertEquals($user->lastname, $userrow[5]); + $this->assertEquals($user->city, $userrow[6]); + $this->assertEquals('United Kingdom', $userrow[7]); + $this->assertEquals($user->description, $userrow[8]); + $this->assertEquals($user->firstnamephonetic, $userrow[9]); + $this->assertEquals($user->lastnamephonetic, $userrow[10]); + $this->assertEquals($user->middlename, $userrow[11]); + $this->assertEquals($user->alternatename, $userrow[12]); + $this->assertEquals($user->idnumber, $userrow[13]); + $this->assertEquals($user->institution, $userrow[14]); + $this->assertEquals($user->department, $userrow[15]); + $this->assertEquals($user->phone1, $userrow[16]); + $this->assertEquals($user->phone2, $userrow[17]); + $this->assertEquals($user->address, $userrow[18]); + $this->assertEmpty($userrow[19]); + $this->assertEquals('No', $userrow[20]); + $this->assertEquals('Yes', $userrow[21]); + $this->assertEquals($user->moodlenetprofile, $userrow[22]); + $this->assertNotEmpty($userrow[23]); + $this->assertEquals('Horses', $userrow[24]); + $this->assertStringContainsString('Horses', $userrow[25]); } /** @@ -106,6 +166,202 @@ class users_test extends core_reportbuilder_testcase { */ public function datasource_filters_provider(): array { return [ + // User. + 'Filter user' => ['user:userselect', [ + 'user:userselect_operator' => user_filter::USER_SELECT, + 'user:userselect_value' => [-1], + ], false], + 'Filter fullname' => ['user:fullname', [ + 'user:fullname_operator' => text::CONTAINS, + 'user:fullname_value' => 'Zoe', + ], true], + 'Filter fullname (no match)' => ['user:fullname', [ + 'user:fullname_operator' => text::CONTAINS, + 'user:fullname_value' => 'Alfie', + ], false], + 'Filter firstname' => ['user:firstname', [ + 'user:firstname_operator' => text::IS_EQUAL_TO, + 'user:firstname_value' => 'Zoe', + ], true], + 'Filter firstname (no match)' => ['user:firstname', [ + 'user:firstname_operator' => text::IS_EQUAL_TO, + 'user:firstname_value' => 'Alfie', + ], false], + 'Filter middlename' => ['user:middlename', [ + 'user:middlename_operator' => text::IS_EQUAL_TO, + 'user:middlename_value' => 'Zebediah', + ], true], + 'Filter middlename (no match)' => ['user:middlename', [ + 'user:middlename_operator' => text::IS_EQUAL_TO, + 'user:middlename_value' => 'Aardvark', + ], false], + 'Filter lastname' => ['user:lastname', [ + 'user:lastname_operator' => text::IS_EQUAL_TO, + 'user:lastname_value' => 'Zebra', + ], true], + 'Filter lastname (no match)' => ['user:lastname', [ + 'user:lastname_operator' => text::IS_EQUAL_TO, + 'user:lastname_value' => 'Aardvark', + ], false], + 'Filter firstnamephonetic' => ['user:firstnamephonetic', [ + 'user:firstnamephonetic_operator' => text::IS_EQUAL_TO, + 'user:firstnamephonetic_value' => 'Eoz', + ], true], + 'Filter firstnamephonetic (no match)' => ['user:firstnamephonetic', [ + 'user:firstnamephonetic_operator' => text::IS_EQUAL_TO, + 'user:firstnamephonetic_value' => 'Alfie', + ], false], + 'Filter lastnamephonetic' => ['user:lastnamephonetic', [ + 'user:lastnamephonetic_operator' => text::IS_EQUAL_TO, + 'user:lastnamephonetic_value' => 'Arbez', + ], true], + 'Filter lastnamephonetic (no match)' => ['user:lastnamephonetic', [ + 'user:lastnamephonetic_operator' => text::IS_EQUAL_TO, + 'user:lastnamephonetic_value' => 'Aardvark', + ], false], + 'Filter alternatename' => ['user:alternatename', [ + 'user:alternatename_operator' => text::IS_EQUAL_TO, + 'user:alternatename_value' => 'Zee', + ], true], + 'Filter alternatename (no match)' => ['user:alternatename', [ + 'user:alternatename_operator' => text::IS_EQUAL_TO, + 'user:alternatename_value' => 'Aardvark', + ], false], + 'Filter email' => ['user:email', [ + 'user:email_operator' => text::CONTAINS, + 'user:email_value' => 'zoe1', + ], true], + 'Filter email (no match)' => ['user:email', [ + 'user:email_operator' => text::CONTAINS, + 'user:email_value' => 'alfie1', + ], false], + 'Filter phone1' => ['user:phone1', [ + 'user:phone1_operator' => text::IS_EQUAL_TO, + 'user:phone1_value' => '111', + ], true], + 'Filter phone1 (no match)' => ['user:phone1', [ + 'user:phone1_operator' => text::IS_EQUAL_TO, + 'user:phone1_value' => '119', + ], false], + 'Filter phone2' => ['user:phone2', [ + 'user:phone2_operator' => text::IS_EQUAL_TO, + 'user:phone2_value' => '222', + ], true], + 'Filter phone2 (no match)' => ['user:phone2', [ + 'user:phone2_operator' => text::IS_EQUAL_TO, + 'user:phone2_value' => '229', + ], false], + 'Filter address' => ['user:address', [ + 'user:address_operator' => text::IS_EQUAL_TO, + 'user:address_value' => 'Big Farm', + ], true], + 'Filter address (no match)' => ['user:address', [ + 'user:address_operator' => text::IS_EQUAL_TO, + 'user:address_value' => 'Small Farm', + ], false], + + 'Filter city' => ['user:city', [ + 'user:city_operator' => text::IS_EQUAL_TO, + 'user:city_value' => 'Barcelona', + ], true], + 'Filter city (no match)' => ['user:city', [ + 'user:city_operator' => text::IS_EQUAL_TO, + 'user:city_value' => 'Perth', + ], false], + 'Filter country' => ['user:country', [ + 'user:country_operator' => select::EQUAL_TO, + 'user:country_value' => 'ES', + ], true], + 'Filter country (no match)' => ['user:country', [ + 'user:country_operator' => select::EQUAL_TO, + 'user:country_value' => 'AU', + ], false], + 'Filter description' => ['user:description', [ + 'user:description_operator' => text::CONTAINS, + 'user:description_value' => 'Hello there', + ], true], + 'Filter description (no match)' => ['user:description', [ + 'user:description_operator' => text::CONTAINS, + 'user:description_value' => 'Goodbye', + ], false], + 'Filter auth' => ['user:auth', [ + 'user:auth_operator' => select::EQUAL_TO, + 'user:auth_value' => 'manual', + ], true], + 'Filter auth (no match)' => ['user:auth', [ + 'user:auth_operator' => select::EQUAL_TO, + 'user:auth_value' => 'ldap', + ], false], + 'Filter username' => ['user:username', [ + 'user:username_operator' => text::IS_EQUAL_TO, + 'user:username_value' => 'zoe1', + ], true], + 'Filter username (no match)' => ['user:username', [ + 'user:username_operator' => text::IS_EQUAL_TO, + 'user:username_value' => 'alfie1', + ], false], + 'Filter idnumber' => ['user:idnumber', [ + 'user:idnumber_operator' => text::IS_EQUAL_TO, + 'user:idnumber_value' => 'Z0001', + ], true], + 'Filter idnumber (no match)' => ['user:idnumber', [ + 'user:idnumber_operator' => text::IS_EQUAL_TO, + 'user:idnumber_value' => 'A0001', + ], false], + 'Filter institution' => ['user:institution', [ + 'user:institution_operator' => text::IS_EQUAL_TO, + 'user:institution_value' => 'Farm', + ], true], + 'Filter institution (no match)' => ['user:institution', [ + 'user:institution_operator' => text::IS_EQUAL_TO, + 'user:institution_value' => 'University', + ], false], + 'Filter department' => ['user:department', [ + 'user:department_operator' => text::IS_EQUAL_TO, + 'user:department_value' => 'Stable', + ], true], + 'Filter department (no match)' => ['user:department', [ + 'user:department_operator' => text::IS_EQUAL_TO, + 'user:department_value' => 'Office', + ], false], + 'Filter moodlenetprofile' => ['user:moodlenetprofile', [ + 'user:moodlenetprofile_operator' => text::IS_EQUAL_TO, + 'user:moodlenetprofile_value' => '@zoe1@example.com', + ], true], + 'Filter moodlenetprofile (no match)' => ['user:moodlenetprofile', [ + 'user:moodlenetprofile_operator' => text::IS_EQUAL_TO, + 'user:moodlenetprofile_value' => '@alfie1@example.com', + ], false], + 'Filter suspended' => ['user:suspended', [ + 'user:suspended_operator' => boolean_select::NOT_CHECKED, + ], true], + 'Filter suspended (no match)' => ['user:suspended', [ + 'user:suspended_operator' => boolean_select::CHECKED, + ], false], + 'Filter confirmed' => ['user:confirmed', [ + 'user:confirmed_operator' => boolean_select::CHECKED, + ], true], + 'Filter confirmed (no match)' => ['user:confirmed', [ + 'user:confirmed_operator' => boolean_select::NOT_CHECKED, + ], false], + 'Filter timecreated' => ['user:timecreated', [ + 'user:timecreated_operator' => date::DATE_RANGE, + 'user:timecreated_from' => 1622502000, + ], true], + 'Filter timecreated (no match)' => ['user:timecreated', [ + 'user:timecreated_operator' => date::DATE_RANGE, + 'user:timecreated_from' => 1619823600, + 'user:timecreated_to' => 1622502000, + ], false], + 'Filter lastaccess' => ['user:lastaccess', [ + 'user:lastaccess_operator' => date::DATE_EMPTY, + ], true], + 'Filter lastaccess (no match)' => ['user:lastaccess', [ + 'user:lastaccess_operator' => date::DATE_RANGE, + 'user:lastaccess_from' => 1619823600, + 'user:lastaccess_to' => 1622502000, + ], false], + // Tags. 'Filter tag name' => ['tag:name', [ 'tag:name_operator' => tags::EQUAL_TO, @@ -126,14 +382,30 @@ class users_test extends core_reportbuilder_testcase { * * @dataProvider datasource_filters_provider */ - public function test_datasource_filters( - string $filtername, - array $filtervalues, - bool $expectmatch - ): void { + public function test_datasource_filters(string $filtername, array $filtervalues, bool $expectmatch): void { $this->resetAfterTest(); - $user = $this->getDataGenerator()->create_user(['interests' => ['Horses']]); + $user = $this->getDataGenerator()->create_user([ + 'username' => 'zoe1', + 'email' => 'zoe1@example.com', + 'firstname' => 'Zoe', + 'middlename' => 'Zebediah', + 'lastname' => 'Zebra', + 'firstnamephonetic' => 'Eoz', + 'lastnamephonetic' => 'Arbez', + 'alternatename' => 'Zee', + 'idnumber' => 'Z0001', + 'institution' => 'Farm', + 'department' => 'Stable', + 'phone1' => '111', + 'phone2' => '222', + 'address' => 'Big Farm', + 'city' => 'Barcelona', + 'country' => 'ES', + 'description' => 'Hello there', + 'moodlenetprofile' => '@zoe1@example.com', + 'interests' => ['Horses'], + ]); /** @var core_reportbuilder_generator $generator */ $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); @@ -147,8 +419,11 @@ class users_test extends core_reportbuilder_testcase { $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues); if ($expectmatch) { - $this->assertCount(1, $content); - $this->assertEquals($user->username, reset($content[0])); + $this->assertNotEmpty($content); + + // Merge report usernames into easily traversable array. + $usernames = array_merge(...array_map('array_values', $content)); + $this->assertContains($user->username, $usernames); } else { $this->assertEmpty($content); }