Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 400 and 401]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  declare(strict_types=1);
  18  
  19  namespace core_reportbuilder\local\helpers;
  20  
  21  use core_customfield_generator;
  22  use core_reportbuilder_generator;
  23  use core_reportbuilder_testcase;
  24  use core_reportbuilder\local\entities\course;
  25  use core_reportbuilder\local\filters\boolean_select;
  26  use core_reportbuilder\local\filters\date;
  27  use core_reportbuilder\local\filters\select;
  28  use core_reportbuilder\local\filters\text;
  29  use core_reportbuilder\local\report\column;
  30  use core_reportbuilder\local\report\filter;
  31  use core_course\reportbuilder\datasource\courses;
  32  
  33  defined('MOODLE_INTERNAL') || die();
  34  
  35  global $CFG;
  36  require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
  37  
  38  /**
  39   * Unit tests for custom fields helper
  40   *
  41   * @package     core_reportbuilder
  42   * @covers      \core_reportbuilder\local\helpers\custom_fields
  43   * @copyright   2021 David Matamoros <davidmc@moodle.com>
  44   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  45   */
  46  class custom_fields_test extends core_reportbuilder_testcase {
  47  
  48      /**
  49       * Generate custom fields, one of each type
  50       *
  51       * @return custom_fields
  52       */
  53      private function generate_customfields(): custom_fields {
  54  
  55          /** @var core_customfield_generator $generator */
  56          $generator = $this->getDataGenerator()->get_plugin_generator('core_customfield');
  57          $category = $generator->create_category([
  58              'component' => 'core_course',
  59              'area' => 'course',
  60              'itemid' => 0,
  61              'contextid' => \context_system::instance()->id
  62          ]);
  63  
  64          $generator->create_field(
  65              ['categoryid' => $category->get('id'), 'type' => 'text', 'name' => 'Text', 'shortname' => 'text']);
  66  
  67          $generator->create_field(
  68              ['categoryid' => $category->get('id'), 'type' => 'textarea', 'name' => 'Textarea', 'shortname' => 'textarea']);
  69  
  70          $generator->create_field(
  71              ['categoryid' => $category->get('id'), 'type' => 'checkbox', 'name' => 'Checkbox', 'shortname' => 'checkbox']);
  72  
  73          $generator->create_field(
  74              ['categoryid' => $category->get('id'), 'type' => 'date', 'name' => 'Date', 'shortname' => 'date']);
  75  
  76          $generator->create_field(
  77              ['categoryid' => $category->get('id'), 'type' => 'select', 'name' => 'Select', 'shortname' => 'select',
  78                  'configdata' => ['options' => "Cat\nDog", 'defaultvalue' => 'Cat']]);
  79  
  80          $courseentity = new course();
  81          $coursealias = $courseentity->get_table_alias('course');
  82  
  83          // Create an instance of the customfields helper.
  84          return new custom_fields($coursealias . '.id', $courseentity->get_entity_name(),
  85              'core_course', 'course');
  86      }
  87  
  88      /**
  89       * Test for get_columns
  90       */
  91      public function test_get_columns(): void {
  92          $this->resetAfterTest();
  93  
  94          $customfields = $this->generate_customfields();
  95          $columns = $customfields->get_columns();
  96  
  97          $this->assertCount(5, $columns);
  98          $this->assertContainsOnlyInstancesOf(column::class, $columns);
  99  
 100          [$column0, $column1, $column2, $column3, $column4] = $columns;
 101          $this->assertEqualsCanonicalizing(['Text', 'Textarea', 'Checkbox', 'Date', 'Select'], [
 102              $column0->get_title(), $column1->get_title(), $column2->get_title(), $column3->get_title(), $column4->get_title()
 103          ]);
 104  
 105          $this->assertEquals(column::TYPE_TEXT, $column0->get_type());
 106          $this->assertEquals('course', $column0->get_entity_name());
 107          $this->assertStringStartsWith('LEFT JOIN {customfield_data}', $column0->get_joins()[0]);
 108          // Column of type TEXT is sortable.
 109          $this->assertTrue($column0->get_is_sortable());
 110      }
 111  
 112      /**
 113       * Test for add_join
 114       */
 115      public function test_add_join(): void {
 116          $this->resetAfterTest();
 117  
 118          $customfields = $this->generate_customfields();
 119          $columns = $customfields->get_columns();
 120          $this->assertCount(1, ($columns[0])->get_joins());
 121  
 122          $customfields->add_join('JOIN {test} t ON t.id = id');
 123          $columns = $customfields->get_columns();
 124          $this->assertCount(2, ($columns[0])->get_joins());
 125      }
 126  
 127      /**
 128       * Test for add_joins
 129       */
 130      public function test_add_joins(): void {
 131          $this->resetAfterTest();
 132  
 133          $customfields = $this->generate_customfields();
 134          $columns = $customfields->get_columns();
 135          $this->assertCount(1, ($columns[0])->get_joins());
 136  
 137          $customfields->add_joins(['JOIN {test} t ON t.id = id', 'JOIN {test2} t2 ON t2.id = id']);
 138          $columns = $customfields->get_columns();
 139          $this->assertCount(3, ($columns[0])->get_joins());
 140      }
 141  
 142      /**
 143       * Test for get_filters
 144       */
 145      public function test_get_filters(): void {
 146          $this->resetAfterTest();
 147  
 148          $customfields = $this->generate_customfields();
 149          $filters = $customfields->get_filters();
 150  
 151          $this->assertCount(5, $filters);
 152          $this->assertContainsOnlyInstancesOf(filter::class, $filters);
 153  
 154          [$filter0, $filter1, $filter2, $filter3, $filter4] = $filters;
 155          $this->assertEqualsCanonicalizing(['Text', 'Textarea', 'Checkbox', 'Date', 'Select'], [
 156              $filter0->get_header(), $filter1->get_header(), $filter2->get_header(), $filter3->get_header(), $filter4->get_header()
 157          ]);
 158      }
 159  
 160      /**
 161       * Test that adding custom field columns to a report returns expected values
 162       */
 163      public function test_custom_report_content(): void {
 164          $this->resetAfterTest();
 165  
 166          $this->generate_customfields();
 167  
 168          $course = $this->getDataGenerator()->create_course(['customfields' => [
 169              ['shortname' => 'text', 'value' => 'Hello'],
 170              ['shortname' => 'textarea_editor', 'value' => ['text' => 'Goodbye', 'format' => FORMAT_MOODLE]],
 171              ['shortname' => 'checkbox', 'value' => true],
 172              ['shortname' => 'date', 'value' => 1669852800],
 173              ['shortname' => 'select', 'value' => 2],
 174          ]]);
 175  
 176          /** @var core_reportbuilder_generator $generator */
 177          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
 178          $report = $generator->create_report(['name' => 'Courses', 'source' => courses::class, 'default' => 0]);
 179  
 180          // Add user profile field columns to the report.
 181          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname']);
 182          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_text']);
 183          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_textarea']);
 184          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_checkbox']);
 185          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_date']);
 186          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:customfield_select']);
 187  
 188          $content = $this->get_custom_report_content($report->get('id'));
 189  
 190          $this->assertEquals([
 191              $course->fullname,
 192              'Hello',
 193              '<div class="text_to_html">Goodbye</div>',
 194              'Yes',
 195              userdate(1669852800),
 196              'Dog'
 197          ], array_values($content[0]));
 198      }
 199  
 200      /**
 201       * Data provider for {@see test_custom_report_filter}
 202       *
 203       * @return array[]
 204       */
 205      public function custom_report_filter_provider(): array {
 206          return [
 207              'Filter by text custom field' => ['course:customfield_text', [
 208                  'course:customfield_text_operator' => text::IS_EQUAL_TO,
 209                  'course:customfield_text_value' => 'Hello',
 210              ], true],
 211              'Filter by text custom field (no match)' => ['course:customfield_text', [
 212                  'course:customfield_text_operator' => text::IS_EQUAL_TO,
 213                  'course:customfield_text_value' => 'Goodbye',
 214              ], false],
 215              'Filter by textarea custom field' => ['course:customfield_textarea', [
 216                  'course:customfield_textarea_operator' => text::IS_EQUAL_TO,
 217                  'course:customfield_textarea_value' => 'Goodbye',
 218              ], true],
 219              'Filter by textarea custom field (no match)' => ['course:customfield_textarea', [
 220                  'course:customfield_textarea_operator' => text::IS_EQUAL_TO,
 221                  'course:customfield_textarea_value' => 'Hello',
 222              ], false],
 223              'Filter by checkbox custom field' => ['course:customfield_checkbox', [
 224                  'course:customfield_checkbox_operator' => boolean_select::CHECKED,
 225              ], true],
 226              'Filter by checkbox custom field (no match)' => ['course:customfield_checkbox', [
 227                  'course:customfield_checkbox_operator' => boolean_select::NOT_CHECKED,
 228              ], false],
 229              'Filter by date custom field' => ['course:customfield_date', [
 230                  'course:customfield_date_operator' => date::DATE_RANGE,
 231                  'course:customfield_date_from' => 1622502000,
 232              ], true],
 233              'Filter by date custom field (no match)' => ['course:customfield_date', [
 234                  'course:customfield_date_operator' => date::DATE_RANGE,
 235                  'course:customfield_date_to' => 1622502000,
 236              ], false],
 237              'Filter by select custom field' => ['course:customfield_select', [
 238                  'course:customfield_select_operator' => select::EQUAL_TO,
 239                  'course:customfield_select_value' => 2,
 240              ], true],
 241              'Filter by select custom field (no match)' => ['course:customfield_select', [
 242                  'course:customfield_select_operator' => select::EQUAL_TO,
 243                  'course:customfield_select_value' => 1,
 244              ], false],
 245          ];
 246      }
 247  
 248      /**
 249       * Test filtering report by custom fields
 250       *
 251       * @param string $filtername
 252       * @param array $filtervalues
 253       * @param bool $expectmatch
 254       *
 255       * @dataProvider custom_report_filter_provider
 256       */
 257      public function test_custom_report_filter(string $filtername, array $filtervalues, bool $expectmatch): void {
 258          $this->resetAfterTest();
 259  
 260          $this->generate_customfields();
 261  
 262          $course = $this->getDataGenerator()->create_course(['customfields' => [
 263              ['shortname' => 'text', 'value' => 'Hello'],
 264              ['shortname' => 'textarea_editor', 'value' => ['text' => 'Goodbye', 'format' => FORMAT_MOODLE]],
 265              ['shortname' => 'checkbox', 'value' => true],
 266              ['shortname' => 'date', 'value' => 1669852800],
 267              ['shortname' => 'select', 'value' => 2],
 268          ]]);
 269  
 270          /** @var core_reportbuilder_generator $generator */
 271          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
 272  
 273          // Create report containing single column, and given filter.
 274          $report = $generator->create_report(['name' => 'Users', 'source' => courses::class, 'default' => 0]);
 275          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname']);
 276  
 277          // Add filter, set it's values.
 278          $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]);
 279          $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues);
 280  
 281          if ($expectmatch) {
 282              $this->assertCount(1, $content);
 283              $this->assertEquals($course->fullname, reset($content[0]));
 284          } else {
 285              $this->assertEmpty($content);
 286          }
 287      }
 288  
 289      /**
 290       * Stress test course datasource using custom fields
 291       *
 292       * In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
 293       */
 294      public function test_stress_datasource(): void {
 295          if (!PHPUNIT_LONGTEST) {
 296              $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
 297          }
 298  
 299          $this->resetAfterTest();
 300  
 301          $this->generate_customfields();
 302          $course = $this->getDataGenerator()->create_course(['customfields' => [
 303              ['shortname' => 'text', 'value' => 'Hello'],
 304              ['shortname' => 'textarea_editor', 'value' => ['text' => 'Goodbye', 'format' => FORMAT_MOODLE]],
 305              ['shortname' => 'checkbox', 'value' => true],
 306              ['shortname' => 'date', 'value' => 1669852800],
 307              ['shortname' => 'select', 'value' => 2],
 308          ]]);
 309  
 310          $this->datasource_stress_test_columns(courses::class);
 311          $this->datasource_stress_test_columns_aggregation(courses::class);
 312          $this->datasource_stress_test_conditions(courses::class, 'course:idnumber');
 313      }
 314  }
 315