Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 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  /**
  18   * Unit tests for /lib/formslib.php.
  19   *
  20   * @package   core_form
  21   * @category  phpunit
  22   * @copyright 2011 Sam Hemelryk
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  require_once($CFG->libdir . '/formslib.php');
  30  require_once($CFG->libdir . '/form/radio.php');
  31  require_once($CFG->libdir . '/form/select.php');
  32  require_once($CFG->libdir . '/form/text.php');
  33  
  34  
  35  class core_formslib_testcase extends advanced_testcase {
  36  
  37      public function test_require_rule() {
  38          global $CFG;
  39  
  40          $strictformsrequired = null;
  41          if (isset($CFG->strictformsrequired)) {
  42              $strictformsrequired = $CFG->strictformsrequired;
  43          }
  44  
  45          $rule = new MoodleQuickForm_Rule_Required();
  46  
  47          // First run the tests with strictformsrequired off.
  48          $CFG->strictformsrequired = false;
  49          // Passes.
  50          $this->assertTrue($rule->validate('Something'));
  51          $this->assertTrue($rule->validate("Something\nmore"));
  52          $this->assertTrue($rule->validate("\nmore"));
  53          $this->assertTrue($rule->validate(" more "));
  54          $this->assertTrue($rule->validate('ш'));
  55          $this->assertTrue($rule->validate("の"));
  56          $this->assertTrue($rule->validate("0"));
  57          $this->assertTrue($rule->validate(0));
  58          $this->assertTrue($rule->validate(true));
  59          $this->assertTrue($rule->validate(' '));
  60          $this->assertTrue($rule->validate('      '));
  61          $this->assertTrue($rule->validate("\t"));
  62          $this->assertTrue($rule->validate("\n"));
  63          $this->assertTrue($rule->validate("\r"));
  64          $this->assertTrue($rule->validate("\r\n"));
  65          $this->assertTrue($rule->validate(" \t  \n  \r "));
  66          $this->assertTrue($rule->validate('<p></p>'));
  67          $this->assertTrue($rule->validate('<p> </p>'));
  68          $this->assertTrue($rule->validate('<p>x</p>'));
  69          $this->assertTrue($rule->validate('<img src="smile.jpg" alt="smile" />'));
  70          $this->assertTrue($rule->validate('<img src="smile.jpg" alt="smile"/>'));
  71          $this->assertTrue($rule->validate('<img src="smile.jpg" alt="smile"></img>'));
  72          $this->assertTrue($rule->validate('<hr />'));
  73          $this->assertTrue($rule->validate('<hr/>'));
  74          $this->assertTrue($rule->validate('<hr>'));
  75          $this->assertTrue($rule->validate('<hr></hr>'));
  76          $this->assertTrue($rule->validate('<br />'));
  77          $this->assertTrue($rule->validate('<br/>'));
  78          $this->assertTrue($rule->validate('<br>'));
  79          $this->assertTrue($rule->validate('&nbsp;'));
  80          // Fails.
  81          $this->assertFalse($rule->validate(''));
  82          $this->assertFalse($rule->validate(false));
  83          $this->assertFalse($rule->validate(null));
  84  
  85          // Now run the same tests with it on to make sure things work as expected.
  86          $CFG->strictformsrequired = true;
  87          // Passes.
  88          $this->assertTrue($rule->validate('Something'));
  89          $this->assertTrue($rule->validate("Something\nmore"));
  90          $this->assertTrue($rule->validate("\nmore"));
  91          $this->assertTrue($rule->validate(" more "));
  92          $this->assertTrue($rule->validate("0"));
  93          $this->assertTrue($rule->validate('ш'));
  94          $this->assertTrue($rule->validate("の"));
  95          $this->assertTrue($rule->validate(0));
  96          $this->assertTrue($rule->validate(true));
  97          $this->assertTrue($rule->validate('<p>x</p>'));
  98          $this->assertTrue($rule->validate('<img src="smile.jpg" alt="smile" />'));
  99          $this->assertTrue($rule->validate('<img src="smile.jpg" alt="smile"/>'));
 100          $this->assertTrue($rule->validate('<img src="smile.jpg" alt="smile"></img>'));
 101          $this->assertTrue($rule->validate('<hr />'));
 102          $this->assertTrue($rule->validate('<hr/>'));
 103          $this->assertTrue($rule->validate('<hr>'));
 104          $this->assertTrue($rule->validate('<hr></hr>'));
 105          // Fails.
 106          $this->assertFalse($rule->validate(' '));
 107          $this->assertFalse($rule->validate('      '));
 108          $this->assertFalse($rule->validate("\t"));
 109          $this->assertFalse($rule->validate("\n"));
 110          $this->assertFalse($rule->validate("\r"));
 111          $this->assertFalse($rule->validate("\r\n"));
 112          $this->assertFalse($rule->validate(" \t  \n  \r "));
 113          $this->assertFalse($rule->validate('<p></p>'));
 114          $this->assertFalse($rule->validate('<p> </p>'));
 115          $this->assertFalse($rule->validate('<br />'));
 116          $this->assertFalse($rule->validate('<br/>'));
 117          $this->assertFalse($rule->validate('<br>'));
 118          $this->assertFalse($rule->validate('&nbsp;'));
 119          $this->assertFalse($rule->validate(''));
 120          $this->assertFalse($rule->validate(false));
 121          $this->assertFalse($rule->validate(null));
 122  
 123          if (isset($strictformsrequired)) {
 124              $CFG->strictformsrequired = $strictformsrequired;
 125          }
 126      }
 127  
 128      public function test_range_rule() {
 129          global $CFG;
 130  
 131          require_once('HTML/QuickForm/Rule/Range.php'); // Requires this pear stuff.
 132  
 133          $strictformsrequired = null;
 134          if (isset($CFG->strictformsrequired)) {
 135              $strictformsrequired = $CFG->strictformsrequired;
 136          }
 137  
 138          $rule = new HTML_QuickForm_Rule_Range();
 139  
 140          // First run the tests with strictformsrequired off.
 141          $CFG->strictformsrequired = false;
 142          // Passes.
 143          $rule->setName('minlength'); // Let's verify some min lengths.
 144          $this->assertTrue($rule->validate('12', 2));
 145          $this->assertTrue($rule->validate('123', 2));
 146          $this->assertTrue($rule->validate('áé', 2));
 147          $this->assertTrue($rule->validate('áéí', 2));
 148          $rule->setName('maxlength'); // Let's verify some max lengths.
 149          $this->assertTrue($rule->validate('1', 2));
 150          $this->assertTrue($rule->validate('12', 2));
 151          $this->assertTrue($rule->validate('á', 2));
 152          $this->assertTrue($rule->validate('áé', 2));
 153          $rule->setName('----'); // Let's verify some ranges.
 154          $this->assertTrue($rule->validate('', array(0, 2)));
 155          $this->assertTrue($rule->validate('1', array(0, 2)));
 156          $this->assertTrue($rule->validate('12', array(0, 2)));
 157          $this->assertTrue($rule->validate('á', array(0, 2)));
 158          $this->assertTrue($rule->validate('áé', array(0, 2)));
 159  
 160          // Fail.
 161          $rule->setName('minlength'); // Let's verify some min lengths.
 162          $this->assertFalse($rule->validate('', 2));
 163          $this->assertFalse($rule->validate('1', 2));
 164          $this->assertFalse($rule->validate('á', 2));
 165          $rule->setName('maxlength'); // Let's verify some max lengths.
 166          $this->assertFalse($rule->validate('123', 2));
 167          $this->assertFalse($rule->validate('áéí', 2));
 168          $rule->setName('----'); // Let's verify some ranges.
 169          $this->assertFalse($rule->validate('', array(1, 2)));
 170          $this->assertFalse($rule->validate('123', array(1, 2)));
 171          $this->assertFalse($rule->validate('áéí', array(1, 2)));
 172  
 173          // Now run the same tests with it on to make sure things work as expected.
 174          $CFG->strictformsrequired = true;
 175          // Passes.
 176          $rule->setName('minlength'); // Let's verify some min lengths.
 177          $this->assertTrue($rule->validate('12', 2));
 178          $this->assertTrue($rule->validate('123', 2));
 179          $this->assertTrue($rule->validate('áé', 2));
 180          $this->assertTrue($rule->validate('áéí', 2));
 181          $rule->setName('maxlength'); // Let's verify some min lengths.
 182          $this->assertTrue($rule->validate('1', 2));
 183          $this->assertTrue($rule->validate('12', 2));
 184          $this->assertTrue($rule->validate('á', 2));
 185          $this->assertTrue($rule->validate('áé', 2));
 186          $rule->setName('----'); // Let's verify some ranges.
 187          $this->assertTrue($rule->validate('', array(0, 2)));
 188          $this->assertTrue($rule->validate('1', array(0, 2)));
 189          $this->assertTrue($rule->validate('12', array(0, 2)));
 190          $this->assertTrue($rule->validate('á', array(0, 2)));
 191          $this->assertTrue($rule->validate('áé', array(0, 2)));
 192  
 193          // Fail.
 194          $rule->setName('minlength'); // Let's verify some min lengths.
 195          $this->assertFalse($rule->validate('', 2));
 196          $this->assertFalse($rule->validate('1', 2));
 197          $this->assertFalse($rule->validate('á', 2));
 198          $rule->setName('maxlength'); // Let's verify some min lengths.
 199          $this->assertFalse($rule->validate('123', 2));
 200          $this->assertFalse($rule->validate('áéí', 2));
 201          $rule->setName('----'); // Let's verify some ranges.
 202          $this->assertFalse($rule->validate('', array(1, 2)));
 203          $this->assertFalse($rule->validate('123', array(1, 2)));
 204          $this->assertFalse($rule->validate('áéí', array(1, 2)));
 205  
 206          if (isset($strictformsrequired)) {
 207              $CFG->strictformsrequired = $strictformsrequired;
 208          }
 209      }
 210  
 211      public function test_generate_id_select() {
 212          $el = new MoodleQuickForm_select('choose_one', 'Choose one',
 213              array(1 => 'One', '2' => 'Two'));
 214          $el->_generateId();
 215          $this->assertSame('id_choose_one', $el->getAttribute('id'));
 216      }
 217  
 218      public function test_generate_id_like_repeat() {
 219          $el = new MoodleQuickForm_text('text[7]', 'Type something');
 220          $el->_generateId();
 221          $this->assertSame('id_text_7', $el->getAttribute('id'));
 222      }
 223  
 224      public function test_can_manually_set_id() {
 225          $el = new MoodleQuickForm_text('elementname', 'Type something',
 226              array('id' => 'customelementid'));
 227          $el->_generateId();
 228          $this->assertSame('customelementid', $el->getAttribute('id'));
 229      }
 230  
 231      public function test_generate_id_radio() {
 232          $el = new MoodleQuickForm_radio('radio', 'Label', 'Choice label', 'choice_value');
 233          $el->_generateId();
 234          $this->assertSame('id_radio_choice_value', $el->getAttribute('id'));
 235      }
 236  
 237      public function test_radio_can_manually_set_id() {
 238          $el = new MoodleQuickForm_radio('radio2', 'Label', 'Choice label', 'choice_value',
 239              array('id' => 'customelementid2'));
 240          $el->_generateId();
 241          $this->assertSame('customelementid2', $el->getAttribute('id'));
 242      }
 243  
 244      public function test_generate_id_radio_like_repeat() {
 245          $el = new MoodleQuickForm_radio('repeatradio[2]', 'Label', 'Choice label', 'val');
 246          $el->_generateId();
 247          $this->assertSame('id_repeatradio_2_val', $el->getAttribute('id'));
 248      }
 249  
 250      public function test_rendering() {
 251          $form = new formslib_test_form();
 252          ob_start();
 253          $form->display();
 254          $html = ob_get_clean();
 255  
 256          $this->assertTag(array('tag'=>'select', 'id'=>'id_choose_one',
 257              'attributes'=>array('name'=>'choose_one')), $html);
 258  
 259          $this->assertTag(array('tag'=>'input', 'id'=>'id_text_0',
 260              'attributes'=>array('type'=>'text', 'name'=>'text[0]')), $html);
 261  
 262          $this->assertTag(array('tag'=>'input', 'id'=>'id_text_1',
 263              'attributes'=>array('type'=>'text', 'name'=>'text[1]')), $html);
 264  
 265          $this->assertTag(array('tag'=>'input', 'id'=>'id_radio_choice_value',
 266              'attributes'=>array('type'=>'radio', 'name'=>'radio', 'value'=>'choice_value')), $html);
 267  
 268          $this->assertTag(array('tag'=>'input', 'id'=>'customelementid2',
 269              'attributes'=>array('type'=>'radio', 'name'=>'radio2')), $html);
 270  
 271          $this->assertTag(array('tag'=>'input', 'id'=>'id_repeatradio_0_2',
 272              'attributes'=>array('type'=>'radio', 'name'=>'repeatradio[0]', 'value'=>'2')), $html);
 273  
 274          $this->assertTag(array('tag'=>'input', 'id'=>'id_repeatradio_2_1',
 275              'attributes'=>array('type'=>'radio', 'name'=>'repeatradio[2]', 'value'=>'1')), $html);
 276  
 277          $this->assertTag(array('tag'=>'input', 'id'=>'id_repeatradio_2_2',
 278              'attributes'=>array('type'=>'radio', 'name'=>'repeatradio[2]', 'value'=>'2')), $html);
 279      }
 280  
 281      public function test_settype_debugging_text() {
 282          $mform = new formslib_settype_debugging_text();
 283          $this->assertDebuggingCalled("Did you remember to call setType() for 'texttest'? Defaulting to PARAM_RAW cleaning.");
 284  
 285          // Check form still there though.
 286          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="texttest/');
 287          $mform->display();
 288      }
 289  
 290      public function test_settype_debugging_hidden() {
 291          $mform = new formslib_settype_debugging_hidden();
 292          $this->assertDebuggingCalled("Did you remember to call setType() for 'hiddentest'? Defaulting to PARAM_RAW cleaning.");
 293  
 294          // Check form still there though.
 295          $this->expectOutputRegex('/<input[^>]*name="hiddentest[^>]*type="hidden/');
 296          $mform->display();
 297      }
 298  
 299      public function test_settype_debugging_url() {
 300          $this->resetAfterTest(true);
 301          $this->setAdminUser();
 302          $mform = new formslib_settype_debugging_url();
 303          $this->assertDebuggingCalled("Did you remember to call setType() for 'urltest'? Defaulting to PARAM_RAW cleaning.");
 304  
 305          // Check form still there though.
 306          $this->expectOutputRegex('/<input[^>]*type="url[^>]*name="urltest"/');
 307          $mform->display();
 308      }
 309  
 310      public function test_settype_debugging_repeat() {
 311          $mform = new formslib_settype_debugging_repeat();
 312          $this->assertDebuggingCalled("Did you remember to call setType() for 'repeattest[0]'? Defaulting to PARAM_RAW cleaning.");
 313  
 314          // Check form still there though.
 315          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="repeattest/');
 316          $mform->display();
 317      }
 318  
 319      public function test_settype_debugging_repeat_ok() {
 320          $mform = new formslib_settype_debugging_repeat_ok();
 321          // No debugging expected here.
 322  
 323          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="repeattest/');
 324          $mform->display();
 325      }
 326  
 327      public function test_settype_debugging_group() {
 328          $mform = new formslib_settype_debugging_group();
 329          $this->assertDebuggingCalled("Did you remember to call setType() for 'groupel1'? Defaulting to PARAM_RAW cleaning.");
 330          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="groupel1"/');
 331          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="groupel2"/');
 332          $mform->display();
 333      }
 334  
 335      public function test_settype_debugging_namedgroup() {
 336          $mform = new formslib_settype_debugging_namedgroup();
 337          $this->assertDebuggingCalled("Did you remember to call setType() for 'namedgroup[groupel1]'? Defaulting to PARAM_RAW cleaning.");
 338          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="namedgroup\[groupel1\]"/');
 339          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="namedgroup\[groupel2\]"/');
 340          $mform->display();
 341      }
 342  
 343      public function test_settype_debugging_funky_name() {
 344          $mform = new formslib_settype_debugging_funky_name();
 345          $this->assertDebuggingCalled("Did you remember to call setType() for 'blah[foo][bar][1]'? Defaulting to PARAM_RAW cleaning.");
 346          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="blah\[foo\]\[bar\]\[0\]"/');
 347          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="blah\[foo\]\[bar\]\[1\]"/');
 348          $mform->display();
 349      }
 350  
 351      public function test_settype_debugging_type_inheritance() {
 352          $mform = new formslib_settype_debugging_type_inheritance();
 353          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="blah\[foo\]\[bar\]\[0\]"/');
 354          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="blah\[bar\]\[foo\]\[1\]"/');
 355          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="blah\[any\]\[other\]\[2\]"/');
 356          $mform->display();
 357      }
 358  
 359      public function test_settype_debugging_type_group_in_repeat() {
 360          $mform = new formslib_settype_debugging_type_group_in_repeat();
 361          $this->assertDebuggingCalled("Did you remember to call setType() for 'test2[0]'? Defaulting to PARAM_RAW cleaning.");
 362          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="test1\[0\]"/');
 363          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="test2\[0\]"/');
 364          $mform->display();
 365      }
 366  
 367      public function test_settype_debugging_type_namedgroup_in_repeat() {
 368          $mform = new formslib_settype_debugging_type_namedgroup_in_repeat();
 369          $this->assertDebuggingCalled("Did you remember to call setType() for 'namedgroup[0][test2]'? Defaulting to PARAM_RAW cleaning.");
 370          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="namedgroup\[0\]\[test1\]"/');
 371          $this->expectOutputRegex('/<input[^>]*type="text[^>]*name="namedgroup\[0\]\[test2\]"/');
 372          $mform->display();
 373      }
 374  
 375      public function test_type_cleaning() {
 376          $expectedtypes = array(
 377              'simpleel' => PARAM_INT,
 378              'groupel1' => PARAM_INT,
 379              'groupel2' => PARAM_FLOAT,
 380              'groupel3' => PARAM_INT,
 381              'namedgroup' => array(
 382                  'sndgroupel1' => PARAM_INT,
 383                  'sndgroupel2' => PARAM_FLOAT,
 384                  'sndgroupel3' => PARAM_INT
 385              ),
 386              'namedgroupinherit' => array(
 387                  'thdgroupel1' => PARAM_INT,
 388                  'thdgroupel2' => PARAM_INT
 389              ),
 390              'repeatedel' => array(
 391                  0 => PARAM_INT,
 392                  1 => PARAM_INT
 393              ),
 394              'repeatedelinherit' => array(
 395                  0 => PARAM_INT,
 396                  1 => PARAM_INT
 397              ),
 398              'squaretest' => array(
 399                  0 => PARAM_INT
 400              ),
 401              'nested' => array(
 402                  0 => array(
 403                      'bob' => array(
 404                          123 => PARAM_INT,
 405                          'foo' => PARAM_FLOAT
 406                      ),
 407                      'xyz' => PARAM_RAW
 408                  ),
 409                  1 => PARAM_INT
 410              ),
 411              'repeatgroupel1' => array(
 412                  0 => PARAM_INT,
 413                  1 => PARAM_INT
 414              ),
 415              'repeatgroupel2' => array(
 416                  0 => PARAM_INT,
 417                  1 => PARAM_INT
 418              ),
 419              'repeatnamedgroup' => array(
 420                  0 => array(
 421                      'repeatnamedgroupel1' => PARAM_INT,
 422                      'repeatnamedgroupel2' => PARAM_INT
 423                  ),
 424                  1 => array(
 425                      'repeatnamedgroupel1' => PARAM_INT,
 426                      'repeatnamedgroupel2' => PARAM_INT
 427                  )
 428              )
 429          );
 430          $valuessubmitted = array(
 431              'simpleel' => '11.01',
 432              'groupel1' => '11.01',
 433              'groupel2' => '11.01',
 434              'groupel3' => '11.01',
 435              'namedgroup' => array(
 436                  'sndgroupel1' => '11.01',
 437                  'sndgroupel2' => '11.01',
 438                  'sndgroupel3' => '11.01'
 439              ),
 440              'namedgroupinherit' => array(
 441                  'thdgroupel1' => '11.01',
 442                  'thdgroupel2' => '11.01'
 443              ),
 444              'repeatedel' => array(
 445                  0 => '11.01',
 446                  1 => '11.01'
 447              ),
 448              'repeatedelinherit' => array(
 449                  0 => '11.01',
 450                  1 => '11.01'
 451              ),
 452              'squaretest' => array(
 453                  0 => '11.01'
 454              ),
 455              'nested' => array(
 456                  0 => array(
 457                      'bob' => array(
 458                          123 => '11.01',
 459                          'foo' => '11.01'
 460                      ),
 461                      'xyz' => '11.01'
 462                  ),
 463                  1 => '11.01'
 464              ),
 465              'repeatgroupel1' => array(
 466                  0 => '11.01',
 467                  1 => '11.01'
 468              ),
 469              'repeatgroupel2' => array(
 470                  0 => '11.01',
 471                  1 => '11.01'
 472              ),
 473              'repeatnamedgroup' => array(
 474                  0 => array(
 475                      'repeatnamedgroupel1' => '11.01',
 476                      'repeatnamedgroupel2' => '11.01'
 477                  ),
 478                  1 => array(
 479                      'repeatnamedgroupel1' => '11.01',
 480                      'repeatnamedgroupel2' => '11.01'
 481                  )
 482              )
 483          );
 484          $expectedvalues = array(
 485              'simpleel' => 11,
 486              'groupel1' => 11,
 487              'groupel2' => 11.01,
 488              'groupel3' => 11,
 489              'namedgroup' => array(
 490                  'sndgroupel1' => 11,
 491                  'sndgroupel2' => 11.01,
 492                  'sndgroupel3' => 11
 493              ),
 494              'namedgroupinherit' => array(
 495                  'thdgroupel1' => 11,
 496                  'thdgroupel2' => 11
 497              ),
 498              'repeatable' => 2,
 499              'repeatedel' => array(
 500                  0 => 11,
 501                  1 => 11
 502              ),
 503              'repeatableinherit' => 2,
 504              'repeatedelinherit' => array(
 505                  0 => 11,
 506                  1 => 11
 507              ),
 508              'squaretest' => array(
 509                  0 => 11
 510              ),
 511              'nested' => array(
 512                  0 => array(
 513                      'bob' => array(
 514                          123 => 11,
 515                          'foo' => 11.01
 516                      ),
 517                      'xyz' => '11.01'
 518                  ),
 519                  1 => 11
 520              ),
 521              'repeatablegroup' => 2,
 522              'repeatgroupel1' => array(
 523                  0 => 11,
 524                  1 => 11
 525              ),
 526              'repeatgroupel2' => array(
 527                  0 => 11,
 528                  1 => 11
 529              ),
 530              'repeatablenamedgroup' => 2,
 531              'repeatnamedgroup' => array(
 532                  0 => array(
 533                      'repeatnamedgroupel1' => 11,
 534                      'repeatnamedgroupel2' => 11
 535                  ),
 536                  1 => array(
 537                      'repeatnamedgroupel1' => 11,
 538                      'repeatnamedgroupel2' => 11
 539                  )
 540              )
 541          );
 542  
 543          $mform = new formslib_clean_value();
 544          $mform->get_form()->updateSubmission($valuessubmitted, null);
 545          foreach ($expectedtypes as $elementname => $expected) {
 546              $actual = $mform->get_form()->getCleanType($elementname, $valuessubmitted[$elementname]);
 547              $this->assertSame($expected, $actual, "Failed validating clean type of '$elementname'");
 548          }
 549  
 550          $data = $mform->get_data();
 551          $this->assertSame($expectedvalues, (array) $data);
 552      }
 553  
 554      /**
 555       * MDL-52873
 556       */
 557      public function test_multiple_modgrade_fields() {
 558          global $CFG;
 559          $this->resetAfterTest(true);
 560  
 561          $CFG->theme = 'classic';
 562          $form = new formslib_multiple_modgrade_form();
 563          ob_start();
 564          $form->display();
 565          $html = ob_get_clean();
 566  
 567          $this->assertTag(array('id' => 'fitem_fgroup_id_grade1'), $html);
 568          $this->assertTag(array('id' => 'id_grade1_modgrade_type'), $html);
 569          $this->assertTag(array('id' => 'id_grade1_modgrade_point'), $html);
 570          $this->assertTag(array('id' => 'id_grade1_modgrade_scale'), $html);
 571  
 572          $this->assertTag(array('id' => 'fitem_fgroup_id_grade2'), $html);
 573          $this->assertTag(array('id' => 'id_grade2_modgrade_type'), $html);
 574          $this->assertTag(array('id' => 'id_grade2_modgrade_point'), $html);
 575          $this->assertTag(array('id' => 'id_grade2_modgrade_scale'), $html);
 576  
 577          $this->assertTag(array('id' => 'fitem_fgroup_id_grade_3'), $html);
 578          $this->assertTag(array('id' => 'id_grade_3_modgrade_type'), $html);
 579          $this->assertTag(array('id' => 'id_grade_3_modgrade_point'), $html);
 580          $this->assertTag(array('id' => 'id_grade_3_modgrade_scale'), $html);
 581      }
 582  
 583      /**
 584       * Test persistant freeze elements have different id's.
 585       */
 586      public function test_persistantrreeze_element() {
 587          global $CFG;
 588          $this->resetAfterTest(true);
 589          $CFG->theme = 'classic';
 590  
 591          $form = new formslib_persistantrreeze_element();
 592          ob_start();
 593          $form->display();
 594          $html = ob_get_clean();
 595  
 596          // Test advcheckbox id's.
 597          $this->assertTag(array('id' => 'id_advcheckboxpersistant'), $html);
 598          $this->assertTag(array('id' => 'id_advcheckboxnotpersistant'), $html);
 599          $this->assertNotTag(array('id' => 'id_advcheckboxnotpersistant_persistant'), $html);
 600          $this->assertTag(array('id' => 'id_advcheckboxfrozen'), $html);
 601  
 602          // Check text element id's.
 603          $this->assertTag(array('id' => 'id_textpersistant'), $html);
 604          $this->assertTag(array('id' => 'id_textnotpersistant'), $html);
 605          $this->assertNotTag(array('id' => 'id_textnotpersistant_persistant'), $html);
 606          $this->assertTag(array('id' => 'id_textfrozen'), $html);
 607          $this->assertNotTag(array('id' => 'id_textfrozen_persistant'), $html);
 608  
 609      }
 610  
 611      /**
 612       * Ensure a validation can run at least once per object. See MDL-56259.
 613       */
 614      public function test_multiple_validation() {
 615          $this->resetAfterTest(true);
 616  
 617          // It should be valid.
 618          formslib_multiple_validation_form::mock_submit(['somenumber' => '10']);
 619          $form = new formslib_multiple_validation_form();
 620          $this->assertTrue($form->is_validated());
 621          $this->assertEquals(10, $form->get_data()->somenumber);
 622  
 623          // It should not validate.
 624          formslib_multiple_validation_form::mock_submit(['somenumber' => '-5']);
 625          $form = new formslib_multiple_validation_form();
 626          $this->assertFalse($form->is_validated());
 627          $this->assertNull($form->get_data());
 628      }
 629  
 630      /**
 631       * MDL-56233 - Tests mocking a form inside a namespace.
 632       */
 633      public function test_mock_submit() {
 634          require_once (__DIR__.'/fixtures/namespaced_form.php');
 635          \local_unittests\namespaced_form\exampleform::mock_submit(['title' => 'Mocked Value']);
 636          $form = new \local_unittests\namespaced_form\exampleform();
 637  
 638          // Here is the problem, this is the expected hidden field name.
 639          $expected = '_qf__local_unittests_namespaced_form_exampleform';
 640          self::assertArrayHasKey($expected, $_POST);
 641  
 642          // This should work now, before it would fail.
 643          self::assertTrue($form->is_submitted());
 644          self::assertSame('Mocked Value', $form->get_data()->title);
 645      }
 646  }
 647  
 648  
 649  /**
 650   * Test form to be used by {@link formslib_test::test_rendering()}.
 651   */
 652  class formslib_test_form extends moodleform {
 653      public function definition() {
 654          $this->_form->addElement('select', 'choose_one', 'Choose one',
 655              array(1 => 'One', '2' => 'Two'));
 656  
 657          $repeatels = array(
 658              $this->_form->createElement('text', 'text', 'Type something')
 659          );
 660          // TODO: The repeat_elements() is far from perfect. Everything should be
 661          // repeated auto-magically by default with options only defining exceptions.
 662          // Surely this is caused because we are storing some element information OUT
 663          // from the element (type...) at form level. Anyway, the method should do its
 664          // work better, no matter of that.
 665          $this->repeat_elements($repeatels, 2, array('text' => array('type' => PARAM_RAW)), 'numtexts', 'addtexts');
 666  
 667          $this->_form->addElement('radio', 'radio', 'Label', 'Choice label', 'choice_value');
 668  
 669          $this->_form->addElement('radio', 'radio2', 'Label', 'Choice label', 'choice_value',
 670              array('id' => 'customelementid2'));
 671  
 672          $repeatels = array(
 673              $this->_form->createElement('radio', 'repeatradio', 'Choose {no}', 'One', 1),
 674              $this->_form->createElement('radio', 'repeatradio', 'Choose {no}', 'Two', 2),
 675          );
 676          $this->repeat_elements($repeatels, 3, array(), 'numradios', 'addradios');
 677      }
 678  }
 679  
 680  /**
 681   * Used to test debugging is called when text added without setType.
 682   */
 683  class formslib_settype_debugging_text extends moodleform {
 684      public function definition() {
 685          $mform = $this->_form;
 686  
 687          $mform->addElement('text', 'texttest', 'test123', 'testing123');
 688      }
 689  }
 690  
 691  /**
 692   * Used to test debugging is called when hidden added without setType.
 693   */
 694  class formslib_settype_debugging_hidden extends moodleform {
 695      public function definition() {
 696          $mform = $this->_form;
 697  
 698          $mform->addElement('hidden', 'hiddentest', '1');
 699      }
 700  }
 701  
 702  /**
 703   * Used to test debugging is called when hidden added without setType.
 704   */
 705  class formslib_settype_debugging_url extends moodleform {
 706      public function definition() {
 707          $mform = $this->_form;
 708  
 709          $mform->addElement('url', 'urltest', 'urltest');
 710      }
 711  }
 712  
 713  /**
 714   * Used to test debugging is called when repeated text added without setType.
 715   */
 716  class formslib_settype_debugging_repeat extends moodleform {
 717      public function definition() {
 718          $mform = $this->_form;
 719  
 720          $repeatels = array(
 721              $mform->createElement('text', 'repeattest', 'Type something')
 722          );
 723  
 724          $this->repeat_elements($repeatels, 1, array(), 'numtexts', 'addtexts');
 725      }
 726  }
 727  
 728  /**
 729   * Used to no debugging is called when correctly test.
 730   */
 731  class formslib_settype_debugging_repeat_ok extends moodleform {
 732      public function definition() {
 733          $mform = $this->_form;
 734  
 735          $repeatels = array(
 736              $mform->createElement('text', 'repeattest', 'Type something')
 737          );
 738  
 739          $this->repeat_elements($repeatels, 2, array('repeattest' => array('type' => PARAM_RAW)), 'numtexts', 'addtexts');
 740      }
 741  }
 742  
 743  /**
 744   * Used to test if debugging is called when a group contains elements without type.
 745   */
 746  class formslib_settype_debugging_group extends moodleform {
 747      public function definition() {
 748          $mform = $this->_form;
 749          $group = array(
 750              $mform->createElement('text', 'groupel1', 'groupel1'),
 751              $mform->createElement('text', 'groupel2', 'groupel2')
 752          );
 753          $mform->addGroup($group);
 754          $mform->setType('groupel2', PARAM_INT);
 755      }
 756  }
 757  
 758  /**
 759   * Used to test if debugging is called when a named group contains elements without type.
 760   */
 761  class formslib_settype_debugging_namedgroup extends moodleform {
 762      public function definition() {
 763          $mform = $this->_form;
 764          $group = array(
 765              $mform->createElement('text', 'groupel1', 'groupel1'),
 766              $mform->createElement('text', 'groupel2', 'groupel2')
 767          );
 768          $mform->addGroup($group, 'namedgroup');
 769          $mform->setType('namedgroup[groupel2]', PARAM_INT);
 770      }
 771  }
 772  
 773  /**
 774   * Used to test if debugging is called when has a funky name.
 775   */
 776  class formslib_settype_debugging_funky_name extends moodleform {
 777      public function definition() {
 778          $mform = $this->_form;
 779          $mform->addElement('text', 'blah[foo][bar][0]', 'test', 'test');
 780          $mform->addElement('text', 'blah[foo][bar][1]', 'test', 'test');
 781          $mform->setType('blah[foo][bar][0]', PARAM_INT);
 782      }
 783  }
 784  
 785  /**
 786   * Used to test that debugging is not called with type inheritance.
 787   */
 788  class formslib_settype_debugging_type_inheritance extends moodleform {
 789      public function definition() {
 790          $mform = $this->_form;
 791          $mform->addElement('text', 'blah[foo][bar][0]', 'test1', 'test');
 792          $mform->addElement('text', 'blah[bar][foo][1]', 'test2', 'test');
 793          $mform->addElement('text', 'blah[any][other][2]', 'test3', 'test');
 794          $mform->setType('blah[foo][bar]', PARAM_INT);
 795          $mform->setType('blah[bar]', PARAM_FLOAT);
 796          $mform->setType('blah', PARAM_TEXT);
 797      }
 798  }
 799  
 800  /**
 801   * Used to test the debugging when using groups in repeated elements.
 802   */
 803  class formslib_settype_debugging_type_group_in_repeat extends moodleform {
 804      public function definition() {
 805          $mform = $this->_form;
 806          $groupelements = array(
 807              $mform->createElement('text', 'test1', 'test1', 'test'),
 808              $mform->createElement('text', 'test2', 'test2', 'test')
 809          );
 810          $group = $mform->createElement('group', null, 'group1', $groupelements, null, false);
 811          $this->repeat_elements(array($group), 1, array('test1' => array('type' => PARAM_INT)), 'hidden', 'button');
 812      }
 813  }
 814  
 815  /**
 816   * Used to test the debugging when using named groups in repeated elements.
 817   */
 818  class formslib_settype_debugging_type_namedgroup_in_repeat extends moodleform {
 819      public function definition() {
 820          $mform = $this->_form;
 821          $groupelements = array(
 822              $mform->createElement('text', 'test1', 'test1', 'test'),
 823              $mform->createElement('text', 'test2', 'test2', 'test')
 824          );
 825          $group = $mform->createElement('group', 'namedgroup', 'group1', $groupelements, null, true);
 826          $this->repeat_elements(array($group), 1, array('namedgroup[test1]' => array('type' => PARAM_INT)), 'hidden', 'button');
 827      }
 828  }
 829  
 830  /**
 831   * Used to check value cleaning.
 832   */
 833  class formslib_clean_value extends moodleform {
 834      public function get_form() {
 835          return $this->_form;
 836      }
 837      public function definition() {
 838          $mform = $this->_form;
 839  
 840          // Add a simple int.
 841          $mform->addElement('text', 'simpleel', 'simpleel');
 842          $mform->setType('simpleel', PARAM_INT);
 843  
 844          // Add a non-named group.
 845          $group = array(
 846              $mform->createElement('text', 'groupel1', 'groupel1'),
 847              $mform->createElement('text', 'groupel2', 'groupel2'),
 848              $mform->createElement('text', 'groupel3', 'groupel3')
 849          );
 850          $mform->setType('groupel1', PARAM_INT);
 851          $mform->setType('groupel2', PARAM_FLOAT);
 852          $mform->setType('groupel3', PARAM_INT);
 853          $mform->addGroup($group);
 854  
 855          // Add a named group.
 856          $group = array(
 857              $mform->createElement('text', 'sndgroupel1', 'sndgroupel1'),
 858              $mform->createElement('text', 'sndgroupel2', 'sndgroupel2'),
 859              $mform->createElement('text', 'sndgroupel3', 'sndgroupel3')
 860          );
 861          $mform->addGroup($group, 'namedgroup');
 862          $mform->setType('namedgroup[sndgroupel1]', PARAM_INT);
 863          $mform->setType('namedgroup[sndgroupel2]', PARAM_FLOAT);
 864          $mform->setType('namedgroup[sndgroupel3]', PARAM_INT);
 865  
 866          // Add a named group, with inheritance.
 867          $group = array(
 868              $mform->createElement('text', 'thdgroupel1', 'thdgroupel1'),
 869              $mform->createElement('text', 'thdgroupel2', 'thdgroupel2')
 870          );
 871          $mform->addGroup($group, 'namedgroupinherit');
 872          $mform->setType('namedgroupinherit', PARAM_INT);
 873  
 874          // Add a repetition.
 875          $repeat = $mform->createElement('text', 'repeatedel', 'repeatedel');
 876          $this->repeat_elements(array($repeat), 2, array('repeatedel' => array('type' => PARAM_INT)), 'repeatable', 'add', 0);
 877  
 878          // Add a repetition, with inheritance.
 879          $repeat = $mform->createElement('text', 'repeatedelinherit', 'repeatedelinherit');
 880          $this->repeat_elements(array($repeat), 2, array(), 'repeatableinherit', 'add', 0);
 881          $mform->setType('repeatedelinherit', PARAM_INT);
 882  
 883          // Add an arbitrary named element.
 884          $mform->addElement('text', 'squaretest[0]', 'squaretest[0]');
 885          $mform->setType('squaretest[0]', PARAM_INT);
 886  
 887          // Add an arbitrary nested array named element.
 888          $mform->addElement('text', 'nested[0][bob][123]', 'nested[0][bob][123]');
 889          $mform->setType('nested[0][bob][123]', PARAM_INT);
 890  
 891          // Add inheritance test cases.
 892          $mform->setType('nested', PARAM_INT);
 893          $mform->setType('nested[0]', PARAM_RAW);
 894          $mform->setType('nested[0][bob]', PARAM_FLOAT);
 895          $mform->addElement('text', 'nested[1]', 'nested[1]');
 896          $mform->addElement('text', 'nested[0][xyz]', 'nested[0][xyz]');
 897          $mform->addElement('text', 'nested[0][bob][foo]', 'nested[0][bob][foo]');
 898  
 899          // Add group in repeated element (with extra inheritance).
 900          $groupelements = array(
 901              $mform->createElement('text', 'repeatgroupel1', 'repeatgroupel1'),
 902              $mform->createElement('text', 'repeatgroupel2', 'repeatgroupel2')
 903          );
 904          $group = $mform->createElement('group', 'repeatgroup', 'repeatgroup', $groupelements, null, false);
 905          $this->repeat_elements(array($group), 2, array('repeatgroupel1' => array('type' => PARAM_INT),
 906              'repeatgroupel2' => array('type' => PARAM_INT)), 'repeatablegroup', 'add', 0);
 907  
 908          // Add named group in repeated element.
 909          $groupelements = array(
 910              $mform->createElement('text', 'repeatnamedgroupel1', 'repeatnamedgroupel1'),
 911              $mform->createElement('text', 'repeatnamedgroupel2', 'repeatnamedgroupel2')
 912          );
 913          $group = $mform->createElement('group', 'repeatnamedgroup', 'repeatnamedgroup', $groupelements, null, true);
 914          $this->repeat_elements(array($group), 2, array('repeatnamedgroup[repeatnamedgroupel1]' => array('type' => PARAM_INT),
 915              'repeatnamedgroup[repeatnamedgroupel2]' => array('type' => PARAM_INT)), 'repeatablenamedgroup', 'add', 0);
 916      }
 917  }
 918  
 919  /**
 920   * Used to test that modgrade fields get unique id attributes.
 921   */
 922  class formslib_multiple_modgrade_form extends moodleform {
 923      public function definition() {
 924          $mform = $this->_form;
 925          $mform->addElement('modgrade', 'grade1', 'Grade 1');
 926          $mform->addElement('modgrade', 'grade2', 'Grade 2');
 927          $mform->addElement('modgrade', 'grade[3]', 'Grade 3');
 928      }
 929  }
 930  
 931  /**
 932   * Used to test frozen elements get unique id attributes.
 933   */
 934  class formslib_persistantrreeze_element extends moodleform {
 935      public function definition() {
 936          $mform = $this->_form;
 937  
 938          // Create advanced checkbox.
 939          // Persistant.
 940          $advcheckboxpersistant = $mform->addElement('advcheckbox', 'advcheckboxpersistant', 'advcheckbox');
 941          $mform->setType('advcheckboxpersistant', PARAM_BOOL);
 942          $advcheckboxpersistant->setChecked(true);
 943          $advcheckboxpersistant->freeze();
 944          $advcheckboxpersistant->setPersistantFreeze(true);
 945          // Frozen.
 946          $advcheckboxfrozen = $mform->addElement('advcheckbox', 'advcheckboxfrozen', 'advcheckbox');
 947          $mform->setType('advcheckboxfrozen', PARAM_BOOL);
 948          $advcheckboxfrozen->setChecked(true);
 949          $advcheckboxfrozen->freeze();
 950          // Neither persistant nor Frozen.
 951          $mform->addElement('advcheckbox', 'advcheckboxnotpersistant', 'advcheckbox');
 952          $mform->setType('advcheckboxnotpersistant', PARAM_BOOL);
 953  
 954          // Create text fields.
 955          // Persistant.
 956          $elpersistant = $mform->addElement('text', 'textpersistant', 'test', 'test');
 957          $mform->setType('textpersistant', PARAM_TEXT);
 958          $elpersistant->freeze();
 959          $elpersistant->setPersistantFreeze(true);
 960          // Frozen.
 961          $elfrozen = $mform->addElement('text', 'textfrozen', 'test', 'test');
 962          $mform->setType('textfrozen', PARAM_TEXT);
 963          $elfrozen->freeze();
 964          // Neither persistant nor Frozen.
 965          $mform->addElement('text', 'textnotpersistant', 'test', 'test');
 966          $mform->setType('textnotpersistant', PARAM_TEXT);
 967      }
 968  }
 969  
 970  /**
 971   * Used to test that you can validate a form more than once. See MDL-56250.
 972   * @package    core_form
 973   * @author     Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
 974   * @copyright  2016 Catalyst IT
 975   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 976   */
 977  class formslib_multiple_validation_form extends moodleform {
 978      /**
 979       * Simple definition, one text field which can have a number.
 980       */
 981      public function definition() {
 982          $mform = $this->_form;
 983          $mform->addElement('text', 'somenumber');
 984          $mform->setType('somenumber', PARAM_INT);
 985      }
 986  
 987      /**
 988       * The number cannot be negative.
 989       * @param array $data An array of form data
 990       * @param array $files An array of form files
 991       * @return array Error messages
 992       */
 993      public function validation($data, $files) {
 994          $errors = parent::validation($data, $files);
 995          if ($data['somenumber'] < 0) {
 996              $errors['somenumber'] = 'The number cannot be negative.';
 997          }
 998          return $errors;
 999      }
1000  }