Merge branch 'MDL-75345-401' of https://github.com/paulholden/moodle into MOODLE_401_STABLE

This commit is contained in:
Andrew Nicols 2023-02-02 08:17:54 +08:00
commit 3ef9c4c5d0
10 changed files with 1143 additions and 1007 deletions

View File

@ -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');

View File

@ -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;

View File

@ -1,76 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
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 <davidmc@moodle.com>
* @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;
}
}

View File

@ -1,71 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
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 <paulh@moodle.com>
* @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;
}
}

View File

@ -0,0 +1,349 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Unit tests for base entity
*
* @package core_reportbuilder
* @copyright 2021 David Matamoros <davidmc@moodle.com>
* @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 <davidmc@moodle.com>
* @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);
}
}

View File

@ -1,515 +0,0 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
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 <davidmc@moodle.com>
* @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 = '<a href="https://www.example.com/moodle/course/view.php?id=' . $course1->id . '">Course 1</a>';
$this->assertEquals($expected, $courserow['coursefullnamewithlink']);
$expected = '<a href="https://www.example.com/moodle/course/view.php?id=' . $course1->id . '">C1</a>';
$this->assertEquals($expected, $courserow['courseshortnamewithlink']);
$expected = '<a href="https://www.example.com/moodle/course/view.php?id=' . $course1->id . '">IDNumber1</a>';
$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');
}
}

View File

@ -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 <paulh@moodle.com>
* @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('<a href="' . $userprofile . '">' . $userfullname . '</a>', $userrow['fullnamewithlink']);
$this->assertEquals($userpicture . $userfullname, $userrow['fullnamewithpicture']);
$this->assertEquals('<a href="' . $userprofile . '">' . $userpicture . $userfullname . '</a>',
$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();
}
}

View File

@ -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 <davidmc@moodle.com>
* @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);
}
}
}

View File

@ -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]));
}
}

View File

@ -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);
}