MDL-37847 plain text essays were screwing up HTML special chars.
authorTim Hunt <T.J.Hunt@open.ac.uk>
Mon, 4 Feb 2013 12:54:10 +0000 (12:54 +0000)
committerTim Hunt <T.J.Hunt@open.ac.uk>
Mon, 4 Feb 2013 12:54:10 +0000 (12:54 +0000)
This was incorrect use of PARAM_CLEANHTML for these inputs.

This fix also adds some unit tests to try to verify that this does not
break again in future.

question/engine/tests/helpers.php
question/type/essay/question.php
question/type/essay/tests/helper.php [new file with mode: 0644]
question/type/essay/tests/walkthrough_test.php [new file with mode: 0644]

index cc8fbfe..494dbc8 100644 (file)
@@ -619,8 +619,14 @@ abstract class qbehaviour_walkthrough_test_base extends question_testcase {
     protected $displayoptions;
     /** @var question_usage_by_activity */
     protected $quba;
-    /** @var unknown_type integer */
+    /** @var integer */
+
     protected $slot;
+    /**
+     * @var string after {@link render()} has been called, this contains the
+     * display of the question in its current state.
+     */
+    protected $currentoutput = '';
 
     protected function setUp() {
         parent::setUp();
@@ -670,6 +676,14 @@ abstract class qbehaviour_walkthrough_test_base extends question_testcase {
         }
     }
 
+    /**
+     * Generate the HTML rendering of the question in its current state in
+     * $this->currentoutput so that it can be verified.
+     */
+    protected function render() {
+        $this->currentoutput = $this->quba->render_question($this->slot, $this->displayoptions);
+    }
+
     /**
      * @param $condition one or more Expectations. (users varargs).
      */
index fa0ed9b..270c65b 100644 (file)
@@ -56,8 +56,10 @@ class qtype_essay_question extends question_with_responses {
     public function get_expected_data() {
         if ($this->responseformat == 'editorfilepicker') {
             $expecteddata = array('answer' => question_attempt::PARAM_CLEANHTML_FILES);
-        } else {
+        } else if ($this->responseformat == 'editor') {
             $expecteddata = array('answer' => PARAM_CLEANHTML);
+        } else {
+            $expecteddata = array('answer' => PARAM_RAW);
         }
         $expecteddata['answerformat'] = PARAM_ALPHANUMEXT;
         if ($this->attachments != 0) {
diff --git a/question/type/essay/tests/helper.php b/question/type/essay/tests/helper.php
new file mode 100644 (file)
index 0000000..e7484aa
--- /dev/null
@@ -0,0 +1,100 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Test helpers for the essay question type.
+ *
+ * @package    qtype_essay
+ * @copyright  2013 The Open University
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Test helper class for the essay question type.
+ *
+ * @copyright  2013 The Open University
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_essay_test_helper extends question_test_helper {
+    public function get_test_questions() {
+        return array('editor', 'editorfilepicker', 'plain', 'monospaced');
+    }
+
+    /**
+     * Helper method to reduce duplication.
+     * @return qtype_essay_question
+     */
+    protected function initialise_essay_question() {
+        question_bank::load_question_definition_classes('essay');
+        $q = new qtype_essay_question();
+        test_question_maker::initialise_a_question($q);
+        $q->name = 'Essay question (HTML editor)';
+        $q->questiontext = 'Please write a story about a frog.';
+        $q->generalfeedback = 'I hope your story had a beginning, a middle and an end.';
+        $q->responseformat = 'editor';
+        $q->responsefieldlines = 10;
+        $q->attachments = 0;
+        $q->graderinfo = '';
+        $q->graderinfoformat = FORMAT_HTML;
+        $q->qtype = question_bank::get_qtype('essay');
+
+        return $q;
+    }
+
+    /**
+     * Makes an essay question using the HTML editor as input.
+     * @return qtype_essay_question
+     */
+    public function make_essay_question_editor() {
+        return $this->initialise_essay_question();
+    }
+
+    /**
+     * Makes an essay question using the HTML editor allowing embedded files as
+     * input, and up to three attachments.
+     * @return qtype_essay_question
+     */
+    public function make_essay_question_editorfilepicker() {
+        $q = $this->initialise_essay_question();
+        $q->responseformat = 'editorfilepicker';
+        $q->attachments = 3;
+        return $q;
+    }
+
+    /**
+     * Makes an essay question using plain text input.
+     * @return qtype_essay_question
+     */
+    public function make_essay_question_plain() {
+        $q = $this->initialise_essay_question();
+        $q->responseformat = 'plain';
+        return $q;
+    }
+
+    /**
+     * Makes an essay question using monospaced input.
+     * @return qtype_essay_question
+     */
+    public function make_essay_question_monospaced() {
+        $q = $this->initialise_essay_question();
+        $q->responseformat = 'monospaced';
+        return $q;
+    }
+}
diff --git a/question/type/essay/tests/walkthrough_test.php b/question/type/essay/tests/walkthrough_test.php
new file mode 100644 (file)
index 0000000..301edd3
--- /dev/null
@@ -0,0 +1,156 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This file contains tests that walks essay questions through some attempts.
+ *
+ * @package   qtype_essay
+ * @copyright 2013 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
+
+
+/**
+ * Unit tests for the essay question type.
+ *
+ * @copyright 2013 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_essay_walkthrough_test extends qbehaviour_walkthrough_test_base {
+
+    protected function check_contains_textarea($name, $content = '', $height = 10) {
+        $fieldname = $this->quba->get_field_prefix($this->slot) . $name;
+
+        $this->assertTag(array('tag' => 'textarea',
+                'attributes' => array('cols' => '60', 'rows' => $height,
+                        'name' => $fieldname)),
+                $this->currentoutput);
+
+        if ($content) {
+            $this->assertRegExp('/' . preg_quote(s($content), '/') . '/', $this->currentoutput);
+        }
+    }
+
+    public function test_deferred_feedback_html_editor() {
+
+        // Create a matching question.
+        $q = test_question_maker::make_question('essay', 'editor');
+        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
+
+        $prefix = $this->quba->get_field_prefix($this->slot);
+        $fieldname = $prefix . 'answer';
+        $response = '<p>The <b>cat</b> sat on the mat. Then it ate a <b>frog</b>.</p>';
+
+        // Check the initial state.
+        $this->check_current_state(question_state::$todo);
+        $this->check_current_mark(null);
+        $this->render();
+        $this->check_contains_textarea('answer', '');
+        $this->check_current_output(
+                $this->get_contains_question_text_expectation($q),
+                $this->get_does_not_contain_feedback_expectation());
+        $this->check_step_count(1);
+
+        // Save a response.
+        $this->quba->process_all_actions(null, array(
+            'slots'                    => $this->slot,
+            $fieldname                 => $response,
+            $fieldname . 'format'      => FORMAT_HTML,
+            $prefix . ':sequencecheck' => '1',
+        ));
+
+        // Verify.
+        $this->check_current_state(question_state::$complete);
+        $this->check_current_mark(null);
+        $this->check_step_count(2);
+        $this->render();
+        $this->check_contains_textarea('answer', $response);
+        $this->check_current_output(
+                $this->get_contains_question_text_expectation($q),
+                $this->get_does_not_contain_feedback_expectation());
+        $this->check_step_count(2);
+
+        // Finish the attempt.
+        $this->quba->finish_all_questions();
+
+        // Verify.
+        $this->check_current_state(question_state::$needsgrading);
+        $this->check_current_mark(null);
+        $this->render();
+        $this->assertRegExp('/' . preg_quote($response, '/') . '/', $this->currentoutput);
+        $this->check_current_output(
+                $this->get_contains_question_text_expectation($q),
+                $this->get_contains_general_feedback_expectation($q));
+    }
+
+    public function test_deferred_feedback_plain_text() {
+
+        // Create a matching question.
+        $q = test_question_maker::make_question('essay', 'plain');
+        $this->start_attempt_at_question($q, 'deferredfeedback', 1);
+
+        $prefix = $this->quba->get_field_prefix($this->slot);
+        $fieldname = $prefix . 'answer';
+        $response = "x < 1\nx > 0\nFrog & Toad were friends.";
+
+        // Check the initial state.
+        $this->check_current_state(question_state::$todo);
+        $this->check_current_mark(null);
+        $this->render();
+        $this->check_contains_textarea('answer', '');
+        $this->check_current_output(
+                $this->get_contains_question_text_expectation($q),
+                $this->get_does_not_contain_feedback_expectation());
+        $this->check_step_count(1);
+
+        // Save a response.
+        $this->quba->process_all_actions(null, array(
+            'slots'                    => $this->slot,
+            $fieldname                 => $response,
+            $fieldname . 'format'      => FORMAT_HTML,
+            $prefix . ':sequencecheck' => '1',
+        ));
+
+        // Verify.
+        $this->check_current_state(question_state::$complete);
+        $this->check_current_mark(null);
+        $this->check_step_count(2);
+        $this->render();
+        $this->check_contains_textarea('answer', $response);
+        $this->check_current_output(
+                $this->get_contains_question_text_expectation($q),
+                $this->get_does_not_contain_feedback_expectation());
+        $this->check_step_count(2);
+
+        // Finish the attempt.
+        $this->quba->finish_all_questions();
+
+        // Verify.
+        $this->check_current_state(question_state::$needsgrading);
+        $this->check_current_mark(null);
+        $this->render();
+        $this->assertRegExp('/' . preg_quote(s($response), '/') . '/', $this->currentoutput);
+        $this->check_current_output(
+                $this->get_contains_question_text_expectation($q),
+                $this->get_contains_general_feedback_expectation($q));
+    }
+}