diff --git a/question/type/ddimageortext/edit_ddimageortext_form.php b/question/type/ddimageortext/edit_ddimageortext_form.php index 62edd6dec51..63d4c710e4f 100644 --- a/question/type/ddimageortext/edit_ddimageortext_form.php +++ b/question/type/ddimageortext/edit_ddimageortext_form.php @@ -267,8 +267,8 @@ class qtype_ddimageortext_edit_form extends qtype_ddtoimage_edit_form_base { for ($dragindex = 0; $dragindex < $data['noitems']; $dragindex++) { $label = $data['draglabel'][$dragindex]; if ($data['drags'][$dragindex]['dragitemtype'] == 'word') { - $allowedtags = '
'; - $errormessage = get_string('formerror_disallowedtags', 'qtype_ddimageortext'); + $allowedtags = '
'; + $errormessage = get_string('formerror_disallowedtags', 'qtype_ddimageortext', s($allowedtags)); } else { $allowedtags = ''; $errormessage = get_string('formerror_noallowedtags', 'qtype_ddimageortext'); diff --git a/question/type/ddimageortext/lang/en/qtype_ddimageortext.php b/question/type/ddimageortext/lang/en/qtype_ddimageortext.php index 5acf026ce7f..4143d01f86d 100644 --- a/question/type/ddimageortext/lang/en/qtype_ddimageortext.php +++ b/question/type/ddimageortext/lang/en/qtype_ddimageortext.php @@ -37,7 +37,7 @@ $string['draggableword'] = 'Draggable text'; $string['dropbackground'] = 'Background image for dragging markers onto'; $string['dropzone'] = 'Drop zone {$a}'; $string['dropzoneheader'] = 'Drop zones'; -$string['formerror_disallowedtags'] = 'Sorry, HTML tags are not allowed in draggable text.'; +$string['formerror_disallowedtags'] = 'Only "{$a}" tags are allowed in this draggable text.'; $string['formerror_noallowedtags'] = 'HTML tags are not allowed in this text which is the alt text for a draggable image.'; $string['formerror_noytop'] = 'You must provide a value for the y coordinate for the top left corner of this drop area. You can drag and drop the drop area above to set the coordinates or enter them manually here.'; $string['formerror_noxleft'] = 'You must provide a value for the x coordinate for the top left corner of this drop area. You can drag and drop the drop area above to set the coordinates or enter them manually here.'; diff --git a/question/type/ddimageortext/tests/edit_form_test.php b/question/type/ddimageortext/tests/edit_form_test.php new file mode 100644 index 00000000000..4e7b2245611 --- /dev/null +++ b/question/type/ddimageortext/tests/edit_form_test.php @@ -0,0 +1,108 @@ +. + +/** + * Unit tests for the drag-and-drop onto image edit form. + * + * @package qtype_ddimageortext + * @copyright 2019 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'); +require_once($CFG->dirroot . '/question/type/edit_question_form.php'); +require_once($CFG->dirroot . '/question/type/ddimageortext/edit_ddimageortext_form.php'); + +/** + * Unit tests for the drag-and-drop onto image edit form. + * + * @copyright 2019 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddimageortext_edit_form_test extends advanced_testcase { + /** + * Helper method. + * + * @return array with two elements: + * question_edit_form great a question form instance that can be tested. + * stdClass the question category. + */ + protected function get_form() { + $this->setAdminUser(); + $this->resetAfterTest(); + + $syscontext = context_system::instance(); + $category = question_make_default_categories(array($syscontext)); + $fakequestion = new stdClass(); + $fakequestion->qtype = 'ddimageortext'; + $fakequestion->contextid = $syscontext->id; + $fakequestion->createdby = 2; + $fakequestion->category = $category->id; + $fakequestion->questiontext = 'Test question'; + $fakequestion->options = new stdClass(); + $fakequestion->options->answers = array(); + $fakequestion->formoptions = new stdClass(); + $fakequestion->formoptions->movecontext = null; + $fakequestion->formoptions->repeatelements = true; + $fakequestion->inputs = null; + + $form = new qtype_ddimageortext_edit_form(new moodle_url('/'), $fakequestion, $category, + new question_edit_contexts($syscontext)); + + return [$form, $category]; + } + + /** + * Test the form correctly validates the HTML allowed in items. + */ + public function test_item_validation() { + list($form, $category) = $this->get_form(); + + $submitteddata = [ + 'category' => $category->id, + 'bgimage' => '', + 'nodropzone' => 0, + 'noitems' => 5, + 'drags' => [ + ['dragitemtype' => 'image'], + ['dragitemtype' => 'image'], + ['dragitemtype' => 'word'], + ['dragitemtype' => 'word'], + ['dragitemtype' => 'word'], + ], + 'draglabel' => [ + 'frog', + 'toad', + 'cat', + 'chien', + '', + ], + ]; + + $errors = $form->validation($submitteddata, []); + + $this->assertArrayNotHasKey('drags[0]', $errors); + $this->assertEquals('HTML tags are not allowed in this text which is the alt text for a draggable image.', + $errors['drags[1]']); + $this->assertArrayNotHasKey('drags[2]', $errors); + $this->assertArrayNotHasKey('drags[3]', $errors); + $this->assertEquals('Only "<br><sub><sup><b><i><strong><em><span>" ' . + 'tags are allowed in this draggable text.', $errors['drags[4]']); + } +} diff --git a/question/type/ddimageortext/tests/helper.php b/question/type/ddimageortext/tests/helper.php index 62d00d5b0cc..718469f67f7 100644 --- a/question/type/ddimageortext/tests/helper.php +++ b/question/type/ddimageortext/tests/helper.php @@ -34,7 +34,7 @@ defined('MOODLE_INTERNAL') || die(); */ class qtype_ddimageortext_test_helper extends question_test_helper { public function get_test_questions() { - return array('fox', 'maths', 'xsection'); + return array('fox', 'maths', 'xsection', 'mixedlang'); } /** @@ -248,4 +248,39 @@ class qtype_ddimageortext_test_helper extends question_test_helper { return $fromform; } + + /** + * Make a test question where the drag items are a different language than the main question text. + * + * @return qtype_ddimageortext_question + */ + public function make_ddimageortext_question_mixedlang() { + question_bank::load_question_definition_classes('ddimageortext'); + $dd = new qtype_ddimageortext_question(); + + test_question_maker::initialise_a_question($dd); + + $dd->name = 'Question about French in English.'; + $dd->questiontext = '

Complete the blanks in this sentence.

' . + '

J\'ai perdu [[1]] plume de [[2]] tante - l\'avez-vous vue?

'; + $dd->generalfeedback = 'This sentence uses each letter of the alphabet.'; + $dd->qtype = question_bank::get_qtype('ddimageortext'); + + $dd->shufflechoices = true; + + test_question_maker::set_standard_combined_feedback_fields($dd); + + $dd->choices = $this->make_choice_structure(array( + new qtype_ddimageortext_drag_item('la', 1, 1), + new qtype_ddimageortext_drag_item('ma', 2, 1), + )); + + $dd->places = $this->make_place_structure(array( + new qtype_ddimageortext_drop_zone('', 1, 1), + new qtype_ddimageortext_drop_zone('', 2, 1) + )); + $dd->rightchoices = array(1 => 1, 2 => 2); + + return $dd; + } } diff --git a/question/type/ddimageortext/tests/walkthrough_test.php b/question/type/ddimageortext/tests/walkthrough_test.php index e9cad591782..b71e4afb4f9 100644 --- a/question/type/ddimageortext/tests/walkthrough_test.php +++ b/question/type/ddimageortext/tests/walkthrough_test.php @@ -854,4 +854,20 @@ class qtype_ddimageortext_walkthrough_test extends qbehaviour_walkthrough_test_b $this->check_current_state(question_state::$gradedright); $this->check_current_mark(3); } + + public function test_mixed_lang_rendering() { + + // Create a mixe drag-and-drop question. + $dd = test_question_maker::make_question('ddimageortext', 'mixedlang'); + $dd->shufflechoices = false; + $this->start_attempt_at_question($dd, 'interactive', 1); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + new question_pattern_expectation('~
la
~'), + new question_pattern_expectation('~
ma
~') + ); + } } diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index fede3d401b6..f42bd857786 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot.'/question/type/ddimageortext/edit_ddtoimage_form_base.php'); require_once($CFG->dirroot.'/question/type/ddmarker/shapes.php'); -define('QTYPE_DDMARKER_ALLOWED_TAGS_IN_MARKER', '
'); +define('QTYPE_DDMARKER_ALLOWED_TAGS_IN_MARKER', '
'); /** diff --git a/question/type/ddmarker/tests/edit_form_test.php b/question/type/ddmarker/tests/edit_form_test.php new file mode 100644 index 00000000000..45032c54887 --- /dev/null +++ b/question/type/ddmarker/tests/edit_form_test.php @@ -0,0 +1,99 @@ +. + +/** + * Unit tests for the drag-and-drop markers edit form. + * + * @package qtype_ddmarker + * @copyright 2019 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'); +require_once($CFG->dirroot . '/question/type/edit_question_form.php'); +require_once($CFG->dirroot . '/question/type/ddmarker/edit_ddmarker_form.php'); + +/** + * Unit tests for the drag-and-drop markers edit form. + * + * @copyright 2019 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_edit_form_test extends advanced_testcase { + /** + * Helper method. + * + * @return array with two elements: + * question_edit_form great a question form instance that can be tested. + * stdClass the question category. + */ + protected function get_form() { + $this->setAdminUser(); + $this->resetAfterTest(); + + $syscontext = context_system::instance(); + $category = question_make_default_categories(array($syscontext)); + $fakequestion = new stdClass(); + $fakequestion->qtype = 'ddmarker'; + $fakequestion->contextid = $syscontext->id; + $fakequestion->createdby = 2; + $fakequestion->category = $category->id; + $fakequestion->questiontext = 'Test question'; + $fakequestion->options = new stdClass(); + $fakequestion->options->answers = array(); + $fakequestion->formoptions = new stdClass(); + $fakequestion->formoptions->movecontext = null; + $fakequestion->formoptions->repeatelements = true; + $fakequestion->inputs = null; + + $form = new qtype_ddmarker_edit_form(new moodle_url('/'), $fakequestion, $category, + new question_edit_contexts($syscontext)); + + return [$form, $category]; + } + + /** + * Test the form correctly validates the HTML allowed in items. + */ + public function test_item_validation() { + list($form, $category) = $this->get_form(); + + $submitteddata = [ + 'category' => $category->id, + 'bgimage' => 0, + 'nodropzone' => 0, + 'noitems' => 4, + 'drags' => [ + ['label' => 'frog'], + ['label' => 'toad'], + ['label' => 'chien'], + ['label' => ''], + ], + ]; + + $errors = $form->validation($submitteddata, []); + + $this->assertArrayNotHasKey('drags[0]', $errors); + $this->assertArrayNotHasKey('drags[1]', $errors); + $this->assertArrayNotHasKey('drags[2]', $errors); + $this->assertEquals('Only "<br><i><em><b><strong>' . + '<sup><sub><u><span>" tags are allowed in the label for a marker.', + $errors['drags[3]']); + } +} diff --git a/question/type/ddwtos/tests/edit_form_test.php b/question/type/ddwtos/tests/edit_form_test.php index 0e462aa9b5f..e33fb7ae9ed 100644 --- a/question/type/ddwtos/tests/edit_form_test.php +++ b/question/type/ddwtos/tests/edit_form_test.php @@ -41,7 +41,9 @@ class qtype_ddwtos_edit_form_test extends advanced_testcase { * * @param string $classname the question form class to instantiate. * - * @return question_edit_form great a question form instance that can be tested. + * @return array with two elements: + * question_edit_form great a question form instance that can be tested. + * stdClass the question category. */ protected function get_form($classname) { $this->setAdminUser(); @@ -50,7 +52,7 @@ class qtype_ddwtos_edit_form_test extends advanced_testcase { $syscontext = context_system::instance(); $category = question_make_default_categories(array($syscontext)); $fakequestion = new stdClass(); - $fakequestion->qtype = 'stack'; + $fakequestion->qtype = 'ddwtos'; // Does not actually matter if this is wrong. $fakequestion->contextid = $syscontext->id; $fakequestion->createdby = 2; $fakequestion->category = $category->id; @@ -61,15 +63,18 @@ class qtype_ddwtos_edit_form_test extends advanced_testcase { $fakequestion->formoptions->movecontext = null; $fakequestion->formoptions->repeatelements = true; $fakequestion->inputs = null; - return new $classname(new moodle_url('/'), $fakequestion, $category, + + $form = new $classname(new moodle_url('/'), $fakequestion, $category, new question_edit_contexts($syscontext)); + + return [$form, $category]; } /** * Test the form shows the right number of groups of choices. */ public function test_number_of_choice_groups() { - $form = $this->get_form('qtype_ddwtos_edit_form'); + list($form) = $this->get_form('qtype_ddwtos_edit_form'); // Use reflection to get the protected property we need. $property = new ReflectionProperty('qtype_ddwtos_edit_form', '_form'); $property->setAccessible(true); @@ -78,4 +83,32 @@ class qtype_ddwtos_edit_form_test extends advanced_testcase { $groupoptions = $choices->_elements[1]; $this->assertCount(8, $groupoptions->_options); } + + /** + * Test the form correctly validates the HTML allowed in choices. + */ + public function test_choices_validation() { + list($form, $category) = $this->get_form('qtype_ddwtos_edit_form'); + + $submitteddata = [ + 'category' => $category->id, + 'questiontext' => ['text' => 'Test [[1]] question [[2]]', 'format' => FORMAT_HTML], + 'choices' => [ + ['answer' => 'frog'], + ['answer' => 'toad'], + ['answer' => 'chien'], + ['answer' => ''], + ], + ]; + + $errors = $form->validation($submitteddata, []); + + $this->assertArrayNotHasKey('choices[0]', $errors); + $this->assertArrayNotHasKey('choices[1]', $errors); + $this->assertArrayNotHasKey('choices[2]', $errors); + $this->assertEquals('<textarea> is not allowed. ' . + '(Only <sub>, <sup>, <b>, <i>, ' . + '<em>, <strong>, <span> are permitted.)', + $errors['choices[3]']); + } } diff --git a/question/type/gapselect/edit_form_base.php b/question/type/gapselect/edit_form_base.php index fb861d5306c..fa99533ef47 100644 --- a/question/type/gapselect/edit_form_base.php +++ b/question/type/gapselect/edit_form_base.php @@ -40,7 +40,8 @@ class qtype_gapselect_edit_form_base extends question_edit_form { 'b', 'i', 'em', - 'strong' + 'strong', + 'span', ); /** @var string regex to match HTML open tags. */ @@ -55,7 +56,7 @@ class qtype_gapselect_edit_form_base extends question_edit_form { /** * Vaidate some input to make sure it does not contain any tags other than * $this->allowedhtmltags. - * @param unknown_type $text the input to validate. + * @param string $text the input to validate. * @return string any validation errors. */ protected function get_illegal_tag_error($text) { diff --git a/question/type/gapselect/tests/edit_form_test.php b/question/type/gapselect/tests/edit_form_test.php index 6c427355d2c..dee9bd51043 100644 --- a/question/type/gapselect/tests/edit_form_test.php +++ b/question/type/gapselect/tests/edit_form_test.php @@ -65,7 +65,10 @@ class qtype_gapselect_edit_form_test extends advanced_testcase { * * @param string $classname the question form class to instantiate. * - * @return question_edit_form great a question form instance that can be tested. + * + * @return array with two elements: + * question_edit_form great a question form instance that can be tested. + * stdClass the question category. */ protected function get_form($classname) { $this->setAdminUser(); @@ -74,7 +77,7 @@ class qtype_gapselect_edit_form_test extends advanced_testcase { $syscontext = context_system::instance(); $category = question_make_default_categories(array($syscontext)); $fakequestion = new stdClass(); - $fakequestion->qtype = 'stack'; + $fakequestion->qtype = 'gapselect'; // Does not actually matter if this is wrong. $fakequestion->contextid = $syscontext->id; $fakequestion->createdby = 2; $fakequestion->category = $category->id; @@ -85,19 +88,22 @@ class qtype_gapselect_edit_form_test extends advanced_testcase { $fakequestion->formoptions->movecontext = null; $fakequestion->formoptions->repeatelements = true; $fakequestion->inputs = null; - return new $classname(new moodle_url('/'), $fakequestion, $category, + + $form = new $classname(new moodle_url('/'), $fakequestion, $category, new question_edit_contexts($syscontext)); + + return [$form, $category]; } public function test_get_illegal_tag_error() { - $form = $this->get_form('qtype_gapselect_edit_form_base_testable'); + list($form) = $this->get_form('qtype_gapselect_edit_form_base_testable'); $this->assertEquals('', $form->get_illegal_tag_error('frog')); $this->assertEquals('', $form->get_illegal_tag_error('toad')); $a = new stdClass(); $a->tag = '<ijk>'; - $a->allowed = '<sub>, <sup>, <b>, <i>, <em>, <strong>'; + $a->allowed = '<sub>, <sup>, <b>, <i>, <em>, <strong>, <span>'; $this->assertEquals(get_string('tagsnotallowed', 'qtype_gapselect', $a), $form->get_illegal_tag_error('')); $a->tag = '</cat>'; @@ -131,7 +137,7 @@ class qtype_gapselect_edit_form_test extends advanced_testcase { * Test the form shows the right number of groups of choices. */ public function test_number_of_choice_groups() { - $form = $this->get_form('qtype_gapselect_edit_form'); + list($form) = $this->get_form('qtype_gapselect_edit_form'); // Use reflection to get the protected property we need. $property = new ReflectionProperty('qtype_gapselect_edit_form', '_form'); $property->setAccessible(true); @@ -140,4 +146,26 @@ class qtype_gapselect_edit_form_test extends advanced_testcase { $groupoptions = $choices->_elements[1]; $this->assertCount(20, $groupoptions->_options); } + + /** + * Test the form correctly validates the HTML allowed in choices. + */ + public function test_choices_validation() { + list($form, $category) = $this->get_form('qtype_gapselect_edit_form'); + + $submitteddata = [ + 'category' => $category->id, + 'questiontext' => ['text' => 'Test [[1]] question [[2]]', 'format' => FORMAT_HTML], + 'choices' => [ + ['answer' => 'frog'], + ['answer' => 'toad'], + ], + ]; + + $errors = $form->validation($submitteddata, []); + + $this->assertArrayNotHasKey('choices[0]', $errors); + $this->assertEquals('<b> is not allowed. (No HTML is allowed here.)', + $errors['choices[1]']); + } }