Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]

   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  namespace qbank_columnsortorder;
  18  
  19  defined('MOODLE_INTERNAL') || die();
  20  
  21  use advanced_testcase;
  22  use context_course;
  23  use core_question\local\bank\column_base;
  24  use core_question\local\bank\question_edit_contexts;
  25  use core_question\local\bank\view;
  26  use moodle_url;
  27  
  28  global $CFG;
  29  require_once($CFG->dirroot . '/question/tests/fixtures/testable_core_question_column.php');
  30  require_once($CFG->dirroot . '/question/classes/external.php');
  31  
  32  /**
  33   * Test class for columnsortorder feature.
  34   *
  35   * @package    qbank_columnsortorder
  36   * @copyright  2021 Catalyst IT Australia Pty Ltd
  37   * @author     Ghaly Marc-Alexandre <marc-alexandreghaly@catalyst-ca.net>
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   * @covers \qbank_columnsortorder\column_manager
  40   */
  41  class column_manager_test extends advanced_testcase {
  42  
  43      /**
  44       * Generate a course and return a question bank view for the course context.
  45       *
  46       * @return view
  47       */
  48      protected static function get_question_bank(): view {
  49          $course = self::getDataGenerator()->create_course();
  50          $questionbank = new view(
  51              new question_edit_contexts(context_course::instance($course->id)),
  52              new moodle_url('/'),
  53              $course
  54          );
  55          return $questionbank;
  56      }
  57  
  58      /**
  59       * Return an array of visible columns for the question bank.
  60       *
  61       * @return array
  62       */
  63      protected static function get_columns(): array {
  64          $questionbank = self::get_question_bank();
  65          $columns = [];
  66          foreach ($questionbank->get_visiblecolumns() as $column) {
  67              $columns[] = $column->get_column_id();
  68          }
  69          return $columns;
  70      }
  71  
  72      /**
  73       * Provide examples for testing each column setting function, with test data and data format.
  74       *
  75       * @return array[]
  76       */
  77      public static function settings_provider(): array {
  78          return [
  79              'Test set_column_order' => [
  80                  'setting' => 'enabledcol',
  81                  'function' => 'set_column_order',
  82                  'datamethod' => [__CLASS__, 'get_columns'],
  83                  'csv' => true,
  84              ],
  85              'Test set_hidden_columns' => [
  86                  'setting' => 'hiddencols',
  87                  'function' => 'set_hidden_columns',
  88                  'datamethod' => [__CLASS__, 'get_columns'],
  89                  'csv' => true,
  90              ],
  91              'Test set_column_size' => [
  92                  'setting' => 'colsize',
  93                  'function' => 'set_column_size',
  94                  'datamethod' => 'random_string',
  95                  'csv' => false,
  96              ],
  97          ];
  98      }
  99  
 100      /**
 101       * Retrieve data using the specified method.
 102       * This function is used to retrieve data from various data methods defined within this class.
 103       *
 104       * @param array|string $datamethod This can be either a function name or an array containing the class and method name.
 105       * @return array|string The retrieved data as an array or string, depending on the data method used.
 106       */
 107      protected function get_data_from_datamethod(array|string $datamethod): array|string {
 108          return call_user_func($datamethod);
 109      }
 110  
 111  
 112      /**
 113       * Test setting config settings
 114       *
 115       * @dataProvider settings_provider
 116       * @param string $setting The name of the setting being saved
 117       * @param string $function The name of the function being called
 118       * @param array|string $datamethod The property of the test class to pass to the function.
 119       * @param bool $csv True of the data is stored as a comma-separated list.
 120       * @return void
 121       */
 122      public function test_settings(
 123          string $setting,
 124          string $function,
 125          array|string $datamethod,
 126          bool $csv,
 127      ): void {
 128          $data = $this->get_data_from_datamethod($datamethod);
 129          $this->setAdminUser();
 130          $this->resetAfterTest(true);
 131          $this->assertFalse(get_config('qbank_columnsortorder', $setting));
 132          $this->assertEmpty(get_user_preferences('qbank_columnsortorder_' . $setting));
 133          column_manager::{$function}($data, true);
 134          $expected = $csv ? implode(',', $data) : $data;
 135          $this->assertEquals($expected, get_config('qbank_columnsortorder', $setting));
 136          $this->assertEmpty(get_user_preferences('qbank_columnsortorder_' . $setting));
 137      }
 138  
 139      /**
 140       * Test passing null clears the corresponding config setting.
 141       *
 142       * @dataProvider settings_provider
 143       * @param string $setting The name of the setting being saved
 144       * @param string $function The name of the function being called
 145       * @param array|string $datamethod The property of the test class to pass to the function.
 146       * @param bool $csv True of the data is stored as a comma-separated list.
 147       * @return void
 148       */
 149      public function test_reset_settings(
 150          string $setting,
 151          string $function,
 152          array|string $datamethod,
 153          bool $csv,
 154      ): void {
 155          $data = $this->get_data_from_datamethod($datamethod);
 156          $this->setAdminUser();
 157          $this->resetAfterTest(true);
 158          $initial = $csv ? implode(',', $data) : $data;
 159          set_config($setting, $initial, 'qbank_columnsortorder');
 160          $this->assertEquals($initial, get_config('qbank_columnsortorder', $setting));
 161          column_manager::{$function}(null, true);
 162          $this->assertFalse(get_config('qbank_columnsortorder', $setting));
 163      }
 164  
 165      /**
 166       * Test setting user preferences
 167       *
 168       * @dataProvider settings_provider
 169       * @param string $setting The name of the setting being saved
 170       * @param string $function The name of the function being called
 171       * @param array|string $datamethod The property of the test class to pass to the function.
 172       * @param bool $csv True of the data is stored as a comma-separated list.
 173       * @return void
 174       */
 175      public function test_settings_user(
 176          string $setting,
 177          string $function,
 178          array|string $datamethod,
 179          bool $csv,
 180      ): void {
 181          $this->resetAfterTest(true);
 182          $data = $this->get_data_from_datamethod($datamethod);
 183          $this->assertFalse(get_config('qbank_columnsortorder', $setting));
 184          $this->assertEmpty(get_user_preferences('qbank_columnsortorder_' . $setting));
 185          column_manager::{$function}($data);
 186          $expected = $csv ? implode(',', $data) : $data;
 187          $this->assertFalse(get_config('qbank_columnsortorder', $setting));
 188          $this->assertEquals($expected, get_user_preferences('qbank_columnsortorder_' . $setting));
 189      }
 190  
 191      /**
 192       * Test passing null clears the corresponding user preference.
 193       *
 194       * @dataProvider settings_provider
 195       * @param string $setting The name of the setting being saved
 196       * @param string $function The name of the function being called
 197       * @param array|string $datamethod The property of the test class to pass to the function.
 198       * @param bool $csv True of the data is stored as a comma-separated list.
 199       * @return void
 200       */
 201      public function test_reset_user_settings(
 202          string $setting,
 203          string $function,
 204          array|string $datamethod,
 205          bool $csv,
 206      ): void {
 207          $data = $this->get_data_from_datamethod($datamethod);
 208          $this->setAdminUser();
 209          $this->resetAfterTest(true);
 210          $initial = $csv ? implode(',', $data) : $data;
 211          set_user_preference('qbank_columnsortorder_' . $setting, $initial);
 212          $this->assertEquals($initial, get_user_preferences('qbank_columnsortorder_' . $setting));
 213          column_manager::{$function}(null);
 214          $this->assertEmpty(get_user_preferences('qbank_columnsortorder_' . $setting));
 215      }
 216  
 217      /**
 218       * Test function get_columns in helper class, that proper data is returned.
 219       *
 220       * @covers ::get_columns
 221       */
 222      public function test_getcolumns_function(): void {
 223          $this->resetAfterTest(true);
 224          $this->setAdminUser();
 225          $columnmanager = new column_manager(true);
 226          $questionlistcolumns = $columnmanager->get_columns();
 227          $this->assertIsArray($questionlistcolumns);
 228          foreach ($questionlistcolumns as $columnnobject) {
 229              $this->assertObjectHasAttribute('class', $columnnobject);
 230              $this->assertObjectHasAttribute('name', $columnnobject);
 231              $this->assertObjectHasAttribute('colname', $columnnobject);
 232          }
 233      }
 234  
 235      /**
 236       * The get_sorted_columns method should return the provided columns sorted according to enabledcol setting.
 237       *
 238       * @return void
 239       */
 240      public function test_get_sorted_columns(): void {
 241          $this->resetAfterTest(true);
 242          $this->setAdminUser();
 243          $questionbank = $this->get_question_bank();
 244          $columns = $this->get_columns($questionbank);
 245          $neworder = $columns;
 246          shuffle($neworder);
 247          set_config('enabledcol', implode(',', $neworder), 'qbank_columnsortorder');
 248  
 249          $columnmanager = new column_manager(true);
 250          $columnstosort = [];
 251          foreach ($columns as $column) {
 252              $columnstosort[$column] = $column;
 253          }
 254  
 255          $sortedcolumns = $columnmanager->get_sorted_columns($columnstosort);
 256  
 257          $expectedorder = ['core_question\local\bank\checkbox_column' . column_base::ID_SEPARATOR . 'checkbox_column' => 0];
 258          foreach ($neworder as $columnid) {
 259              $expectedorder[$columnid] = $columnid;
 260          }
 261          $this->assertSame($expectedorder, $sortedcolumns);
 262      }
 263  
 264      /**
 265       * Test disabled columns are removed from enabledcol setting and added to disabledcol setting.
 266       *
 267       * @return void
 268       */
 269      public function test_disable_columns(): void {
 270          $this->resetAfterTest(true);
 271          $this->setAdminUser();
 272          $questionbank = $this->get_question_bank();
 273          $columns = $this->get_columns($questionbank);
 274          // Set up enabledcol with all plugins.
 275          set_config('enabledcol', implode(',', $columns), 'qbank_columnsortorder');
 276          $questionbank = $this->get_question_bank();
 277          $columns = $this->get_columns($questionbank);
 278          $columnmanager = new column_manager(true);
 279          $this->assertFalse(get_config('qbank_columnsortorder', 'disabledcol'));
 280  
 281          // Disable a random plugin.
 282          $plugincolumns = array_filter($columns, fn($column) => str_starts_with($column, 'qbank_'));
 283          $randomcolumn = $plugincolumns[array_rand($plugincolumns, 1)];
 284          $randomplugin = explode('\\', $randomcolumn)[0];
 285          $columnmanager->disable_columns($randomplugin);
 286  
 287          // The enabledcol setting should now contain all columns except the disabled plugin.
 288          $expectedconfig = array_filter($columns, fn($column) => !str_starts_with($column, $randomplugin));
 289          sort($expectedconfig);
 290          $newconfig = explode(',', get_config('qbank_columnsortorder', 'enabledcol'));
 291          sort($newconfig);
 292          $this->assertEquals($expectedconfig, $newconfig);
 293          $this->assertNotContains($randomcolumn, $newconfig);
 294          // The disabledcol setting should only contain columns from the disabled plugin.
 295          $disabledconfig = explode(',', get_config('qbank_columnsortorder', 'disabledcol'));
 296          array_walk($disabledconfig, fn($column) => $this->assertStringStartsWith($randomplugin, $column));
 297      }
 298  
 299      /**
 300       * Test enabling and disabling columns through event observers
 301       *
 302       * @covers \qbank_columnsortorder\event\plugin_observer
 303       */
 304      public function test_plugin_enabled_disabled_observers(): void {
 305          $this->resetAfterTest(true);
 306          $this->setAdminUser();
 307          $questionbank = $this->get_question_bank();
 308          $columns = $this->get_columns($questionbank);
 309          $columnmanager = new column_manager(true);
 310          $neworder = $columnmanager->get_sorted_columns($columns);
 311          shuffle($neworder);
 312          $columnmanager::set_column_order($neworder, true);
 313          // Get the list of enabled columns, excluding core columns (we can't disable those).
 314          $currentconfig = get_config('qbank_columnsortorder', 'enabledcol');
 315          $currentconfig = array_filter(explode(',', $currentconfig), fn($class) => !str_starts_with($class, 'core'));
 316          // Pick a column at random and get its plugin name.
 317          $randomcolumnid = $currentconfig[array_rand($currentconfig, 1)];
 318          [$randomcolumnclass] = explode(column_base::ID_SEPARATOR, $randomcolumnid, 2);
 319          [$randomplugintodisable] = explode('\\', $randomcolumnclass);
 320          $olddisabledconfig = get_config('qbank_columnsortorder', 'disabledcol');
 321          \core\event\qbank_plugin_disabled::create_for_plugin($randomplugintodisable)->trigger();
 322          $newdisabledconfig = get_config('qbank_columnsortorder', 'disabledcol');
 323          $this->assertNotEquals($olddisabledconfig, $newdisabledconfig);
 324          \core\event\qbank_plugin_enabled::create_for_plugin($randomplugintodisable)->trigger();
 325          $newdisabledconfig = get_config('qbank_columnsortorder', 'disabledcol');
 326          $this->assertEmpty($newdisabledconfig);
 327          $enabledconfig = get_config('qbank_columnsortorder', 'enabledcol');
 328          $contains = strpos($enabledconfig, $randomplugintodisable);
 329          $this->assertNotFalse($contains);
 330          $this->assertIsInt($contains);
 331      }
 332  
 333      /**
 334       * Test enabled columns are removed from disabledcol setting and added to enabledcol setting.
 335       *
 336       * @return void
 337       */
 338      public function test_enable_columns() {
 339          $this->resetAfterTest(true);
 340          $this->setAdminUser();
 341          $questionbank = $this->get_question_bank();
 342          $columns = $this->get_columns($questionbank);
 343          // Set up disablecol with columns from 2 random plugins, and enabledcol with all other columns.
 344          $plugincolumns = array_filter($columns, fn($column) => str_starts_with($column, 'qbank_'));
 345          $plugins = array_unique(array_map(fn($column) => explode('\\', $column)[0], $plugincolumns));
 346          $randomplugins = array_rand($plugins, 2);
 347          $randomplugin1 = $plugins[$randomplugins[0]];
 348          $randomplugin2 = $plugins[$randomplugins[1]];
 349  
 350          $disabledcols = array_filter(
 351              $columns,
 352              fn($column) => str_starts_with($column, $randomplugin1) || str_starts_with($column, $randomplugin2)
 353          );
 354          $enabledcols = array_diff($columns, $disabledcols);
 355  
 356          set_config('enabledcol', implode(',', $enabledcols), 'qbank_columnsortorder');
 357          set_config('disabledcol', implode(',', $disabledcols), 'qbank_columnsortorder');
 358  
 359          // Enable one of the disabled plugins.
 360          $columnmanager = new column_manager(true);
 361          $columnmanager->enable_columns($randomplugin1);
 362          // The enabledcol setting should now contain all columns except the remaining disabled plugin.
 363          $expectedenabled = array_filter($columns, fn($column) => !str_starts_with($column, $randomplugin2));
 364          $expecteddisabled = array_filter($disabledcols, fn($column) => str_starts_with($column, $randomplugin2));
 365          sort($expectedenabled);
 366          sort($expecteddisabled);
 367          $newenabled = explode(',', get_config('qbank_columnsortorder', 'enabledcol'));
 368          sort($newenabled);
 369          $this->assertEquals($expectedenabled, $newenabled);
 370          $this->assertNotContains(reset($expecteddisabled), $newenabled);
 371          // The disabledcol setting should only contain columns from the remaining disabled plugin.
 372          $newdisabled = explode(',', get_config('qbank_columnsortorder', 'disabledcol'));
 373          array_walk($newdisabled, fn($column) => $this->assertStringStartsWith($randomplugin2, $column));
 374      }
 375  
 376      /**
 377       * Test that get_disabled_columns returns names of all the columns in the disabledcol setting
 378       *
 379       * @return void
 380       */
 381      public function test_get_disabled_columns(): void {
 382          $this->resetAfterTest(true);
 383          $this->setAdminUser();
 384          $questionbank = $this->get_question_bank();
 385          $columns = $this->get_columns($questionbank);
 386          // Set up disablecol with columns from 2 random plugins, and enabledcol with all other columns.
 387          $plugincolumns = array_filter($columns, fn($column) => str_starts_with($column, 'qbank_'));
 388          $randomcolumn = $plugincolumns[array_rand($plugincolumns, 1)];
 389          $randomplugin = explode('\\', $randomcolumn)[0];
 390  
 391          $disabledcols = array_filter($columns, fn($column) => str_starts_with($column, $randomplugin));
 392  
 393          set_config('disabledcol', implode(',', $disabledcols), 'qbank_columnsortorder');
 394  
 395          $columnmanager = new column_manager(true);
 396          $expecteddisablednames = [];
 397          foreach ($disabledcols as $disabledcolid) {
 398              [$columnclass, $columnname] = explode(column_base::ID_SEPARATOR, $disabledcolid, 2);
 399              $columnobject = $columnclass::from_column_name($questionbank, $columnname);
 400              $expecteddisablednames[$disabledcolid] = (object) [
 401                  'disabledname' => $columnobject->get_title(),
 402              ];
 403          }
 404          $disablednames = $columnmanager->get_disabled_columns();
 405          $this->assertEquals($expecteddisablednames, $disablednames);
 406      }
 407  }