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] [Versions 39 and 310]

   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 the essay question definition class.
  19   *
  20   * @package    qtype
  21   * @subpackage essay
  22   * @copyright  2009 The Open University
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  global $CFG;
  30  require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
  31  
  32  
  33  /**
  34   * Unit tests for the matching question definition class.
  35   *
  36   * @copyright  2009 The Open University
  37   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class qtype_essay_question_test extends advanced_testcase {
  40      public function test_get_question_summary() {
  41          $essay = test_question_maker::make_an_essay_question();
  42          $essay->questiontext = 'Hello <img src="http://example.com/globe.png" alt="world" />';
  43          $this->assertEquals('Hello [world]', $essay->get_question_summary());
  44      }
  45  
  46      /**
  47       * Test summarise_response() when teachers view quiz attempts and then
  48       * review them to see what has been saved in the response history table.
  49       *
  50       * @dataProvider summarise_response_provider
  51       * @param int $responserequired
  52       * @param int $attachmentsrequired
  53       * @param string $answertext
  54       * @param int $attachmentuploaded
  55       * @param string $expected
  56       */
  57      public function test_summarise_response(int $responserequired, int $attachmentsrequired,
  58                                              string $answertext, int $attachmentuploaded, string $expected): void {
  59          $this->resetAfterTest();
  60  
  61          // If number of allowed attachments is set to 'Unlimited', generate 10 attachments for testing purpose.
  62          $numberofattachments = ($attachmentsrequired === -1) ? 10 : $attachmentsrequired;
  63  
  64          // Create sample attachments.
  65          $attachments = $this->create_user_and_sample_attachments($numberofattachments);
  66  
  67          // Create the essay question under test.
  68          $essay = test_question_maker::make_an_essay_question();
  69          $essay->start_attempt(new question_attempt_step(), 1);
  70  
  71          $essay->responseformat = 'editor';
  72          $essay->responserequired = $responserequired;
  73          $essay->attachmentsrequired = $attachmentsrequired;
  74  
  75          $this->assertEquals($expected, $essay->summarise_response(
  76              ['answer' => $answertext, 'answerformat' => FORMAT_HTML,  'attachments' => $attachments[$attachmentuploaded]]));
  77      }
  78  
  79      /**
  80       * Data provider for summarise_response() test cases.
  81       *
  82       * @return array List of data sets (test cases)
  83       */
  84      public function summarise_response_provider(): array {
  85          return [
  86              'text input required, not attachments required'  =>
  87                  [1, 0, 'This is the text input for this essay.', 0, 'This is the text input for this essay.'],
  88              'Text input required, one attachments required, one uploaded'  =>
  89                  [1, 1, 'This is the text input for this essay.', 1, 'This is the text input for this essay.Attachments: 0 (1 bytes)'],
  90              'Text input is optional, four attachments required, one uploaded'  => [0, 4, '', 1, 'Attachments: 0 (1 bytes)'],
  91              'Text input is optional, four attachments required, two uploaded'  => [0, 4, '', 2, 'Attachments: 0 (1 bytes), 1 (1 bytes)'],
  92              'Text input is optional, four attachments required, three uploaded'  => [0, 4, '', 3, 'Attachments: 0 (1 bytes), 1 (1 bytes), 2 (1 bytes)'],
  93              'Text input is optional, four attachments required, four uploaded'  => [0, 4, 'I have attached 4 files.', 4,
  94                  'I have attached 4 files.Attachments: 0 (1 bytes), 1 (1 bytes), 2 (1 bytes), 3 (1 bytes)'],
  95              'Text input is optional, unlimited attachments required, one uploaded'  => [0, -1, '', 1, 'Attachments: 0 (1 bytes)'],
  96              'Text input is optional, unlimited attachments required, five uploaded'  => [0, -1, 'I have attached 5 files.', 5,
  97                  'I have attached 5 files.Attachments: 0 (1 bytes), 1 (1 bytes), 2 (1 bytes), 3 (1 bytes), 4 (1 bytes)'],
  98              'Text input is optional, unlimited attachments required, ten uploaded'  =>
  99                  [0, -1, '', 10, 'Attachments: 0 (1 bytes), 1 (1 bytes), 2 (1 bytes), 3 (1 bytes), 4 (1 bytes), ' .
 100                      '5 (1 bytes), 6 (1 bytes), 7 (1 bytes), 8 (1 bytes), 9 (1 bytes)']
 101          ];
 102      }
 103  
 104      public function test_is_same_response() {
 105          $essay = test_question_maker::make_an_essay_question();
 106  
 107          $essay->responsetemplate = '';
 108  
 109          $essay->start_attempt(new question_attempt_step(), 1);
 110  
 111          $this->assertTrue($essay->is_same_response(
 112                  array(),
 113                  array('answer' => '')));
 114  
 115          $this->assertTrue($essay->is_same_response(
 116                  array('answer' => ''),
 117                  array('answer' => '')));
 118  
 119          $this->assertTrue($essay->is_same_response(
 120                  array('answer' => ''),
 121                  array()));
 122  
 123          $this->assertFalse($essay->is_same_response(
 124                  array('answer' => 'Hello'),
 125                  array()));
 126  
 127          $this->assertFalse($essay->is_same_response(
 128                  array('answer' => 'Hello'),
 129                  array('answer' => '')));
 130  
 131          $this->assertFalse($essay->is_same_response(
 132                  array('answer' => 0),
 133                  array('answer' => '')));
 134  
 135          $this->assertFalse($essay->is_same_response(
 136                  array('answer' => ''),
 137                  array('answer' => 0)));
 138  
 139          $this->assertFalse($essay->is_same_response(
 140                  array('answer' => '0'),
 141                  array('answer' => '')));
 142  
 143          $this->assertFalse($essay->is_same_response(
 144                  array('answer' => ''),
 145                  array('answer' => '0')));
 146      }
 147  
 148      public function test_is_same_response_with_template() {
 149          $essay = test_question_maker::make_an_essay_question();
 150  
 151          $essay->responsetemplate = 'Once upon a time';
 152  
 153          $essay->start_attempt(new question_attempt_step(), 1);
 154  
 155          $this->assertTrue($essay->is_same_response(
 156                  array(),
 157                  array('answer' => 'Once upon a time')));
 158  
 159          $this->assertTrue($essay->is_same_response(
 160                  array('answer' => ''),
 161                  array('answer' => 'Once upon a time')));
 162  
 163          $this->assertTrue($essay->is_same_response(
 164                  array('answer' => 'Once upon a time'),
 165                  array('answer' => '')));
 166  
 167          $this->assertTrue($essay->is_same_response(
 168                  array('answer' => ''),
 169                  array()));
 170  
 171          $this->assertTrue($essay->is_same_response(
 172                  array('answer' => 'Once upon a time'),
 173                  array()));
 174  
 175          $this->assertFalse($essay->is_same_response(
 176                  array('answer' => 0),
 177                  array('answer' => '')));
 178  
 179          $this->assertFalse($essay->is_same_response(
 180                  array('answer' => ''),
 181                  array('answer' => 0)));
 182  
 183          $this->assertFalse($essay->is_same_response(
 184                  array('answer' => '0'),
 185                  array('answer' => '')));
 186  
 187          $this->assertFalse($essay->is_same_response(
 188                  array('answer' => ''),
 189                  array('answer' => '0')));
 190      }
 191  
 192      public function test_is_complete_response() {
 193          $this->resetAfterTest(true);
 194  
 195          // Create sample attachments.
 196          $attachments = $this->create_user_and_sample_attachments();
 197  
 198          // Create the essay question under test.
 199          $essay = test_question_maker::make_an_essay_question();
 200          $essay->start_attempt(new question_attempt_step(), 1);
 201  
 202          // Test the "traditional" case, where we must receive a response from the user.
 203          $essay->responserequired = 1;
 204          $essay->attachmentsrequired = 0;
 205          $essay->responseformat = 'editor';
 206  
 207          // The empty string should be considered an incomplete response, as should a lack of a response.
 208          $this->assertFalse($essay->is_complete_response(array('answer' => '')));
 209          $this->assertFalse($essay->is_complete_response(array()));
 210  
 211          // Any nonempty string should be considered a complete response.
 212          $this->assertTrue($essay->is_complete_response(array('answer' => 'A student response.')));
 213          $this->assertTrue($essay->is_complete_response(array('answer' => '0 times.')));
 214          $this->assertTrue($essay->is_complete_response(array('answer' => '0')));
 215  
 216          // Test the case where two files are required.
 217          $essay->attachmentsrequired = 2;
 218  
 219          // Attaching less than two files should result in an incomplete response.
 220          $this->assertFalse($essay->is_complete_response(array('answer' => 'A')));
 221          $this->assertFalse($essay->is_complete_response(
 222                  array('answer' => 'A', 'attachments' => $attachments[0])));
 223          $this->assertFalse($essay->is_complete_response(
 224                  array('answer' => 'A', 'attachments' => $attachments[1])));
 225  
 226          // Anything without response text should result in an incomplete response.
 227          $this->assertFalse($essay->is_complete_response(
 228                  array('answer' => '', 'attachments' => $attachments[2])));
 229  
 230          // Attaching two or more files should result in a complete response.
 231          $this->assertTrue($essay->is_complete_response(
 232                  array('answer' => 'A', 'attachments' => $attachments[2])));
 233          $this->assertTrue($essay->is_complete_response(
 234                  array('answer' => 'A', 'attachments' => $attachments[3])));
 235  
 236          // Test the case in which two files are required, but the inline
 237          // response is optional.
 238          $essay->responserequired = 0;
 239  
 240          $this->assertFalse($essay->is_complete_response(
 241                  array('answer' => '', 'attachments' => $attachments[1])));
 242  
 243          $this->assertTrue($essay->is_complete_response(
 244                  array('answer' => '', 'attachments' => $attachments[2])));
 245  
 246          // Test the case in which both the response and online text are optional.
 247          $essay->attachmentsrequired = 0;
 248  
 249          // Providing no answer and no attachment should result in an incomplete
 250          // response.
 251          $this->assertFalse($essay->is_complete_response(
 252                  array('answer' => '')));
 253          $this->assertFalse($essay->is_complete_response(
 254                  array('answer' => '', 'attachments' => $attachments[0])));
 255  
 256          // Providing an answer _or_ an attachment should result in a complete
 257          // response.
 258          $this->assertTrue($essay->is_complete_response(
 259                  array('answer' => '', 'attachments' => $attachments[1])));
 260          $this->assertTrue($essay->is_complete_response(
 261                  array('answer' => 'Answer text.', 'attachments' => $attachments[0])));
 262  
 263          // Test the case in which we're in "no inline response" mode,
 264          // in which the response is not required (as it's not provided).
 265          $essay->reponserequired = 0;
 266          $essay->responseformat = 'noinline';
 267          $essay->attachmensrequired = 1;
 268  
 269          $this->assertFalse($essay->is_complete_response(
 270                  array()));
 271          $this->assertFalse($essay->is_complete_response(
 272                  array('attachments' => $attachments[0])));
 273  
 274          // Providing an attachment should result in a complete response.
 275          $this->assertTrue($essay->is_complete_response(
 276                  array('attachments' => $attachments[1])));
 277  
 278          // Ensure that responserequired is ignored when we're in inline response mode.
 279          $essay->reponserequired = 1;
 280          $this->assertTrue($essay->is_complete_response(
 281                  array('attachments' => $attachments[1])));
 282  
 283      }
 284  
 285      /**
 286       * test_get_question_definition_for_external_rendering
 287       */
 288      public function test_get_question_definition_for_external_rendering() {
 289          $this->resetAfterTest();
 290  
 291          $essay = test_question_maker::make_an_essay_question();
 292          $essay->start_attempt(new question_attempt_step(), 1);
 293          $qa = test_question_maker::get_a_qa($essay);
 294          $displayoptions = new question_display_options();
 295  
 296          $options = $essay->get_question_definition_for_external_rendering($qa, $displayoptions);
 297          $this->assertNotEmpty($options);
 298          $this->assertEquals('editor', $options['responseformat']);
 299          $this->assertEquals(1, $options['responserequired']);
 300          $this->assertEquals(15, $options['responsefieldlines']);
 301          $this->assertEquals(0, $options['attachments']);
 302          $this->assertEquals(0, $options['attachmentsrequired']);
 303          $this->assertNull($options['maxbytes']);
 304          $this->assertNull($options['filetypeslist']);
 305          $this->assertEquals('', $options['responsetemplate']);
 306          $this->assertEquals(FORMAT_MOODLE, $options['responsetemplateformat']);
 307      }
 308  
 309      /**
 310       * Create sample attachemnts and retun generated attachments.
 311       * @param int $numberofattachments
 312       * @return array
 313       */
 314      private function create_user_and_sample_attachments($numberofattachments = 4) {
 315          // Create a new logged-in user, so we can test responses with attachments.
 316          $user = $this->getDataGenerator()->create_user();
 317          $this->setUser($user);
 318  
 319          // Create sample attachments to use in testing.
 320          $helper = test_question_maker::get_test_helper('essay');
 321          $attachments = [];
 322          for ($i = 0; $i < ($numberofattachments + 1); ++$i) {
 323              $attachments[$i] = $helper->make_attachments_saver($i);
 324          }
 325          return $attachments;
 326      }
 327  }