242951b46eac5ef4ec600e53fbb7b3ae6c69f57a
[moodle.git] / question / type / essay / tests / walkthrough_test.php
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/>.
17 /**
18  * This file contains tests that walks essay questions through some attempts.
19  *
20  * @package   qtype_essay
21  * @copyright 2013 The Open University
22  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
32 /**
33  * Unit tests for the essay question type.
34  *
35  * @copyright 2013 The Open University
36  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class qtype_essay_walkthrough_testcase extends qbehaviour_walkthrough_test_base {
40     protected function check_contains_textarea($name, $content = '', $height = 10) {
41         $fieldname = $this->quba->get_field_prefix($this->slot) . $name;
43         $this->assertTag(array('tag' => 'textarea',
44                 'attributes' => array('cols' => '60', 'rows' => $height,
45                         'name' => $fieldname)),
46                 $this->currentoutput);
48         if ($content) {
49             $this->assertRegExp('/' . preg_quote(s($content), '/') . '/', $this->currentoutput);
50         }
51     }
53     /**
54      * Helper method: Store a test file with a given name and contents in a
55      * draft file area.
56      *
57      * @param int $usercontextid user context id.
58      * @param int $draftitemid draft item id.
59      * @param string $filename filename.
60      * @param string $contents file contents.
61      */
62     protected function save_file_to_draft_area($usercontextid, $draftitemid, $filename, $contents) {
63         $fs = get_file_storage();
65         $filerecord = new stdClass();
66         $filerecord->contextid = $usercontextid;
67         $filerecord->component = 'user';
68         $filerecord->filearea = 'draft';
69         $filerecord->itemid = $draftitemid;
70         $filerecord->filepath = '/';
71         $filerecord->filename = $filename;
72         $fs->create_file_from_string($filerecord, $contents);
73     }
75     public function test_deferred_feedback_html_editor() {
77         $this->setAdminUser();
79         // Create an essay question.
80         $q = test_question_maker::make_question('essay', 'editor');
81         $this->start_attempt_at_question($q, 'deferredfeedback', 1);
83         $prefix = $this->quba->get_field_prefix($this->slot);
84         $fieldname = $prefix . 'answer';
85         $response = '<p>The <b>cat</b> sat on the mat. Then it ate a <b>frog</b>.</p>';
87         // Check the initial state.
88         $this->check_current_state(question_state::$todo);
89         $this->check_current_mark(null);
90         $this->render();
91         $this->check_contains_textarea('answer', '');
92         $this->check_current_output(
93                 $this->get_contains_question_text_expectation($q),
94                 $this->get_does_not_contain_feedback_expectation());
95         $this->check_step_count(1);
97         // Save a response.
98         $this->quba->process_all_actions(null, array(
99             'slots'                    => $this->slot,
100             $fieldname                 => $response,
101             $fieldname . 'format'      => FORMAT_HTML,
102             $prefix . ':sequencecheck' => '1',
103         ));
105         // Verify.
106         $this->check_current_state(question_state::$complete);
107         $this->check_current_mark(null);
108         $this->check_step_count(2);
109         $this->render();
110         $this->check_contains_textarea('answer', $response);
111         $this->check_current_output(
112                 $this->get_contains_question_text_expectation($q),
113                 $this->get_does_not_contain_feedback_expectation());
114         $this->check_step_count(2);
116         // Finish the attempt.
117         $this->quba->finish_all_questions();
119         // Verify.
120         $this->check_current_state(question_state::$needsgrading);
121         $this->check_current_mark(null);
122         $this->render();
123         $this->assertRegExp('/' . preg_quote($response, '/') . '/', $this->currentoutput);
124         $this->check_current_output(
125                 $this->get_contains_question_text_expectation($q),
126                 $this->get_contains_general_feedback_expectation($q));
127     }
129     public function test_deferred_feedback_plain_text() {
131         // Create an essay question.
132         $q = test_question_maker::make_question('essay', 'plain');
133         $this->start_attempt_at_question($q, 'deferredfeedback', 1);
135         $prefix = $this->quba->get_field_prefix($this->slot);
136         $fieldname = $prefix . 'answer';
137         $response = "x < 1\nx > 0\nFrog & Toad were friends.";
139         // Check the initial state.
140         $this->check_current_state(question_state::$todo);
141         $this->check_current_mark(null);
142         $this->render();
143         $this->check_contains_textarea('answer', '');
144         $this->check_current_output(
145                 $this->get_contains_question_text_expectation($q),
146                 $this->get_does_not_contain_feedback_expectation());
147         $this->check_step_count(1);
149         // Save a response.
150         $this->quba->process_all_actions(null, array(
151             'slots'                    => $this->slot,
152             $fieldname                 => $response,
153             $fieldname . 'format'      => FORMAT_HTML,
154             $prefix . ':sequencecheck' => '1',
155         ));
157         // Verify.
158         $this->check_current_state(question_state::$complete);
159         $this->check_current_mark(null);
160         $this->check_step_count(2);
161         $this->render();
162         $this->check_contains_textarea('answer', $response);
163         $this->check_current_output(
164                 $this->get_contains_question_text_expectation($q),
165                 $this->get_does_not_contain_feedback_expectation());
166         $this->check_step_count(2);
168         // Finish the attempt.
169         $this->quba->finish_all_questions();
171         // Verify.
172         $this->check_current_state(question_state::$needsgrading);
173         $this->check_current_mark(null);
174         $this->render();
175         $this->assertRegExp('/' . preg_quote(s($response), '/') . '/', $this->currentoutput);
176         $this->check_current_output(
177                 $this->get_contains_question_text_expectation($q),
178                 $this->get_contains_general_feedback_expectation($q));
179     }
181     public function test_responsetemplate() {
183         $this->setAdminUser();
185         // Create an essay question.
186         $q = test_question_maker::make_question('essay', 'responsetemplate');
187         $this->start_attempt_at_question($q, 'deferredfeedback', 1);
189         $prefix = $this->quba->get_field_prefix($this->slot);
190         $fieldname = $prefix . 'answer';
192         // Check the initial state.
193         $this->check_current_state(question_state::$todo);
194         $this->check_current_mark(null);
195         $this->render();
196         $this->check_contains_textarea('answer', 'Once upon a time');
197         $this->check_current_output(
198                 $this->get_contains_question_text_expectation($q),
199                 $this->get_does_not_contain_feedback_expectation());
200         $this->check_step_count(1);
202         // Save.
203         $this->quba->process_all_actions(null, array(
204             'slots'                    => $this->slot,
205             $fieldname                 => 'Once upon a time there was a little green frog.',
206             $fieldname . 'format'      => FORMAT_HTML,
207             $prefix . ':sequencecheck' => '1',
208         ));
210         // Verify.
211         $this->check_current_state(question_state::$complete);
212         $this->check_current_mark(null);
213         $this->check_step_count(2);
214         $this->render();
215         $this->check_contains_textarea('answer', 'Once upon a time there was a little green frog.');
216         $this->check_current_output(
217                 $this->get_contains_question_text_expectation($q),
218                 $this->get_does_not_contain_feedback_expectation());
219         $this->check_step_count(2);
221         // Finish the attempt.
222         $this->quba->finish_all_questions();
224         // Verify.
225         $this->check_current_state(question_state::$needsgrading);
226         $this->check_current_mark(null);
227         $this->render();
228         $this->assertRegExp('/' . preg_quote(s('Once upon a time there was a little green frog.'), '/') . '/', $this->currentoutput);
229         $this->check_current_output(
230                 $this->get_contains_question_text_expectation($q),
231                 $this->get_contains_general_feedback_expectation($q));
232     }
234     public function test_deferred_feedback_html_editor_with_files_attempt_on_last() {
235         global $CFG, $USER;
237         $this->resetAfterTest(true);
238         $this->setAdminUser();
239         $usercontextid = context_user::instance($USER->id)->id;
240         $fs = get_file_storage();
242         // Create an essay question in the DB.
243         $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
244         $cat = $generator->create_question_category();
245         $question = $generator->create_question('essay', 'editorfilepicker', array('category' => $cat->id));
247         // Start attempt at the question.
248         $q = question_bank::load_question($question->id);
249         $this->start_attempt_at_question($q, 'deferredfeedback', 1);
251         $this->check_current_state(question_state::$todo);
252         $this->check_current_mark(null);
253         $this->check_step_count(1);
255         // Process a response and check the expected result.
256         // First we need to get the draft item ids.
257         $this->render();
258         if (!preg_match('/env=editor&amp;.*?itemid=(\d+)&amp;/', $this->currentoutput, $matches)) {
259             throw new coding_exception('Editor draft item id not found.');
260         }
261         $editordraftid = $matches[1];
262         if (!preg_match('/env=filemanager&amp;action=browse&amp;.*?itemid=(\d+)&amp;/', $this->currentoutput, $matches)) {
263             throw new coding_exception('File manager draft item id not found.');
264         }
265         $attachementsdraftid = $matches[1];
267         $this->save_file_to_draft_area($usercontextid, $editordraftid, 'smile.txt', ':-)');
268         $this->save_file_to_draft_area($usercontextid, $attachementsdraftid, 'greeting.txt', 'Hello world!');
269         $this->process_submission(array(
270                 'answer' => 'Here is a picture: <img src="' . $CFG->wwwroot .
271                                 "/draftfile.php/{$usercontextid}/user/draft/{$editordraftid}/smile.txt" .
272                                 '" alt="smile">.',
273                 'answerformat' => FORMAT_HTML,
274                 'answer:itemid' => $editordraftid,
275                 'attachments' => $attachementsdraftid));
277         $this->check_current_state(question_state::$complete);
278         $this->check_current_mark(null);
279         $this->check_step_count(2);
280         $this->save_quba();
282         // Save the same response again, and verify no new step is created.
283         $this->load_quba();
285         $this->render();
286         if (!preg_match('/env=editor&amp;.*?itemid=(\d+)&amp;/', $this->currentoutput, $matches)) {
287             throw new coding_exception('Editor draft item id not found.');
288         }
289         $editordraftid = $matches[1];
290         if (!preg_match('/env=filemanager&amp;action=browse&amp;.*?itemid=(\d+)&amp;/', $this->currentoutput, $matches)) {
291             throw new coding_exception('File manager draft item id not found.');
292         }
293         $attachementsdraftid = $matches[1];
295         $this->process_submission(array(
296                 'answer' => 'Here is a picture: <img src="' . $CFG->wwwroot .
297                                 "/draftfile.php/{$usercontextid}/user/draft/{$editordraftid}/smile.txt" .
298                                 '" alt="smile">.',
299                 'answerformat' => FORMAT_HTML,
300                 'answer:itemid' => $editordraftid,
301                 'attachments' => $attachementsdraftid));
303         $this->check_current_state(question_state::$complete);
304         $this->check_current_mark(null);
305         $this->check_step_count(2);
307         // Now submit all and finish.
308         $this->finish();
309         $this->check_current_state(question_state::$needsgrading);
310         $this->check_current_mark(null);
311         $this->check_step_count(3);
312         $this->save_quba();
314         // Now start a new attempt based on the old one.
315         $this->load_quba();
316         $oldqa = $this->get_question_attempt();
318         $q = question_bank::load_question($question->id);
319         $this->quba = question_engine::make_questions_usage_by_activity('unit_test',
320                 context_system::instance());
321         $this->quba->set_preferred_behaviour('deferredfeedback');
322         $this->slot = $this->quba->add_question($q, 1);
323         $this->quba->start_question_based_on($this->slot, $oldqa);
325         $this->check_current_state(question_state::$complete);
326         $this->check_current_mark(null);
327         $this->check_step_count(1);
328         $this->save_quba();
330         // Now save the same response again, and ensure that a new step is not created.
331         $this->load_quba();
333         $this->render();
334         if (!preg_match('/env=editor&amp;.*?itemid=(\d+)&amp;/', $this->currentoutput, $matches)) {
335             throw new coding_exception('Editor draft item id not found.');
336         }
337         $editordraftid = $matches[1];
338         if (!preg_match('/env=filemanager&amp;action=browse&amp;.*?itemid=(\d+)&amp;/', $this->currentoutput, $matches)) {
339             throw new coding_exception('File manager draft item id not found.');
340         }
341         $attachementsdraftid = $matches[1];
343         $this->process_submission(array(
344                 'answer' => 'Here is a picture: <img src="' . $CFG->wwwroot .
345                                 "/draftfile.php/{$usercontextid}/user/draft/{$editordraftid}/smile.txt" .
346                                 '" alt="smile">.',
347                 'answerformat' => FORMAT_HTML,
348                 'answer:itemid' => $editordraftid,
349                 'attachments' => $attachementsdraftid));
351         $this->check_current_state(question_state::$complete);
352         $this->check_current_mark(null);
353         $this->check_step_count(1);
354     }
356     public function test_deferred_feedback_html_editor_with_files_attempt_on_last_no_files_uploaded() {
357         global $CFG, $USER;
359         $this->resetAfterTest(true);
360         $this->setAdminUser();
361         $usercontextid = context_user::instance($USER->id)->id;
362         $fs = get_file_storage();
364         // Create an essay question in the DB.
365         $generator = $this->getDataGenerator()->get_plugin_generator('core_question');
366         $cat = $generator->create_question_category();
367         $question = $generator->create_question('essay', 'editorfilepicker', array('category' => $cat->id));
369         // Start attempt at the question.
370         $q = question_bank::load_question($question->id);
371         $this->start_attempt_at_question($q, 'deferredfeedback', 1);
373         $this->check_current_state(question_state::$todo);
374         $this->check_current_mark(null);
375         $this->check_step_count(1);
377         // Process a response and check the expected result.
378         // First we need to get the draft item ids.
379         $this->render();
380         if (!preg_match('/env=editor&amp;.*?itemid=(\d+)&amp;/', $this->currentoutput, $matches)) {
381             throw new coding_exception('Editor draft item id not found.');
382         }
383         $editordraftid = $matches[1];
384         if (!preg_match('/env=filemanager&amp;action=browse&amp;.*?itemid=(\d+)&amp;/', $this->currentoutput, $matches)) {
385             throw new coding_exception('File manager draft item id not found.');
386         }
387         $attachementsdraftid = $matches[1];
389         $this->process_submission(array(
390                 'answer' => 'I refuse to draw you a picture, so there!',
391                 'answerformat' => FORMAT_HTML,
392                 'answer:itemid' => $editordraftid,
393                 'attachments' => $attachementsdraftid));
395         $this->check_current_state(question_state::$complete);
396         $this->check_current_mark(null);
397         $this->check_step_count(2);
398         $this->save_quba();
400         // Now submit all and finish.
401         $this->finish();
402         $this->check_current_state(question_state::$needsgrading);
403         $this->check_current_mark(null);
404         $this->check_step_count(3);
405         $this->save_quba();
407         // Now start a new attempt based on the old one.
408         $this->load_quba();
409         $oldqa = $this->get_question_attempt();
411         $q = question_bank::load_question($question->id);
412         $this->quba = question_engine::make_questions_usage_by_activity('unit_test',
413                 context_system::instance());
414         $this->quba->set_preferred_behaviour('deferredfeedback');
415         $this->slot = $this->quba->add_question($q, 1);
416         $this->quba->start_question_based_on($this->slot, $oldqa);
418         $this->check_current_state(question_state::$complete);
419         $this->check_current_mark(null);
420         $this->check_step_count(1);
421         $this->save_quba();
423         // Check the display.
424         $this->load_quba();
425         $this->render();
426         $this->assertRegExp('/I refuse to draw you a picture, so there!/', $this->currentoutput);
427     }