MDL-66147 mod_assign: Change assign to return calculated instance data
[moodle.git] / mod / assign / tests / locallib_test.php
CommitLineData
47f48152
DW
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 (some of) mod/assign/locallib.php.
19 *
20 * @package mod_assign
21 * @category phpunit
22 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26
27defined('MOODLE_INTERNAL') || die();
28
29global $CFG;
30require_once($CFG->dirroot . '/mod/assign/locallib.php');
31require_once($CFG->dirroot . '/mod/assign/upgradelib.php');
757d5b7c 32require_once($CFG->dirroot . '/mod/assign/tests/generator.php');
47f48152
DW
33
34/**
35 * Unit tests for (some of) mod/assign/locallib.php.
36 *
37 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 */
757d5b7c
AN
40class mod_assign_locallib_testcase extends advanced_testcase {
41
42 // Use the generator helper.
43 use mod_assign_test_generator;
47f48152
DW
44
45 public function test_return_links() {
c2114099 46 global $PAGE;
757d5b7c
AN
47
48 $this->resetAfterTest();
49 $course = $this->getDataGenerator()->create_course();
50
51 $assign = $this->create_instance($course);
52 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
53
54 $assign->register_return_link('RETURNACTION', ['param' => 1]);
a6b8fc78
AN
55 $this->assertEquals('RETURNACTION', $assign->get_return_action());
56 $this->assertEquals(['param' => 1], $assign->get_return_params());
47f48152
DW
57 }
58
59 public function test_get_feedback_plugins() {
757d5b7c
AN
60 $this->resetAfterTest();
61 $course = $this->getDataGenerator()->create_course();
62 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
63
64 $this->setUser($teacher);
65 $assign = $this->create_instance($course);
bd3b3bba 66 $installedplugins = array_keys(core_component::get_plugin_list('assignfeedback'));
11527706
DW
67
68 foreach ($assign->get_feedback_plugins() as $plugin) {
69 $this->assertContains($plugin->get_type(), $installedplugins, 'Feedback plugin not in list of installed plugins');
70 }
47f48152
DW
71 }
72
73 public function test_get_submission_plugins() {
757d5b7c
AN
74 $this->resetAfterTest();
75 $course = $this->getDataGenerator()->create_course();
76 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
77
78 $this->setUser($teacher);
79 $assign = $this->create_instance($course);
bd3b3bba 80 $installedplugins = array_keys(core_component::get_plugin_list('assignsubmission'));
11527706
DW
81
82 foreach ($assign->get_submission_plugins() as $plugin) {
83 $this->assertContains($plugin->get_type(), $installedplugins, 'Submission plugin not in list of installed plugins');
84 }
47f48152
DW
85 }
86
87 public function test_is_blind_marking() {
757d5b7c
AN
88 $this->resetAfterTest();
89 $course = $this->getDataGenerator()->create_course();
90 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
91 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
92
93 $this->setUser($teacher);
94 $assign = $this->create_instance($course, ['blindmarking' => 1]);
47f48152
DW
95 $this->assertEquals(true, $assign->is_blind_marking());
96
97 // Test cannot see student names.
98 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
99 $output = $assign->get_renderer()->render($gradingtable);
100 $this->assertEquals(true, strpos($output, get_string('hiddenuser', 'assign')));
101
102 // Test students cannot reveal identities.
103 $nopermission = false;
757d5b7c
AN
104 $student->ignoresesskey = true;
105 $this->setUser($student);
52f3e060 106 $this->expectException('required_capability_exception');
05a6445a 107 $assign->reveal_identities();
757d5b7c 108 $student->ignoresesskey = false;
47f48152
DW
109
110 // Test teachers cannot reveal identities.
111 $nopermission = false;
757d5b7c
AN
112 $teacher->ignoresesskey = true;
113 $this->setUser($teacher);
52f3e060 114 $this->expectException('required_capability_exception');
05a6445a 115 $assign->reveal_identities();
757d5b7c 116 $teacher->ignoresesskey = false;
47f48152
DW
117
118 // Test sesskey is required.
757d5b7c 119 $this->setUser($teacher);
52f3e060 120 $this->expectException('moodle_exception');
05a6445a 121 $assign->reveal_identities();
47f48152
DW
122
123 // Test editingteacher can reveal identities if sesskey is ignored.
757d5b7c
AN
124 $teacher->ignoresesskey = true;
125 $this->setUser($teacher);
05a6445a 126 $assign->reveal_identities();
47f48152 127 $this->assertEquals(false, $assign->is_blind_marking());
757d5b7c 128 $teacher->ignoresesskey = false;
47f48152
DW
129
130 // Test student names are visible.
131 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
132 $output = $assign->get_renderer()->render($gradingtable);
133 $this->assertEquals(false, strpos($output, get_string('hiddenuser', 'assign')));
134
135 // Set this back to default.
757d5b7c 136 $teacher->ignoresesskey = false;
47f48152
DW
137 }
138
80989850
BH
139 /**
140 * Data provider for test_get_assign_perpage
141 *
142 * @return array Provider data
143 */
144 public function get_assign_perpage_provider() {
145 return array(
146 array(
147 'maxperpage' => -1,
148 'userprefs' => array(
149 -1 => -1,
150 10 => 10,
151 20 => 20,
152 50 => 50,
153 ),
154 ),
155 array(
156 'maxperpage' => 15,
157 'userprefs' => array(
158 -1 => 15,
159 10 => 10,
160 20 => 15,
161 50 => 15,
162 ),
163 ),
164 );
165 }
166
167 /**
168 * Test maxperpage
169 *
170 * @dataProvider get_assign_perpage_provider
171 * @param integer $maxperpage site config value
172 * @param array $userprefs Array of user preferences and expected page sizes
173 */
174 public function test_get_assign_perpage($maxperpage, $userprefs) {
757d5b7c
AN
175 $this->resetAfterTest();
176 $course = $this->getDataGenerator()->create_course();
177 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
178 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
179
180 $this->setUser($teacher);
181 $assign = $this->create_instance($course);
80989850 182
80989850
BH
183 set_config('maxperpage', $maxperpage, 'assign');
184 set_user_preference('assign_perpage', null);
185 $this->assertEquals(10, $assign->get_assign_perpage());
186 foreach ($userprefs as $pref => $perpage) {
187 set_user_preference('assign_perpage', $pref);
188 $this->assertEquals($perpage, $assign->get_assign_perpage());
189 }
190 }
191
a5ff898f
DW
192 /**
193 * Test filter by requires grading.
194 *
195 * This is specifically checking an assignment with no grade to make sure we do not
196 * get an exception thrown when rendering the grading table for this type of assignment.
197 */
198 public function test_gradingtable_filter_by_requiresgrading_no_grade() {
199 global $PAGE;
200
994d0da3
DW
201 $this->resetAfterTest();
202
203 $course = $this->getDataGenerator()->create_course();
204 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
205 $this->setUser($teacher);
206 $assign = $this->create_instance($course, [
207 'assignsubmission_onlinetext_enabled' => 1,
208 'assignfeedback_comments_enabled' => 0,
209 'grade' => GRADE_TYPE_NONE
210 ]);
a5ff898f
DW
211
212 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
213 'id' => $assign->get_course_module()->id,
214 'action' => 'grading',
215 )));
216
217 // Render the table with the requires grading filter.
218 $gradingtable = new assign_grading_table($assign, 1, ASSIGN_FILTER_REQUIRE_GRADING, 0, true);
219 $output = $assign->get_renderer()->render($gradingtable);
220
221 // Test that the filter function does not throw errors for assignments with no grade.
222 $this->assertContains(get_string('nothingtodisplay'), $output);
223 }
224
225
79397b56
DB
226 /**
227 * Test submissions with extension date.
228 */
229 public function test_gradingtable_extension_due_date() {
230 global $PAGE;
231
757d5b7c
AN
232 $this->resetAfterTest();
233 $course = $this->getDataGenerator()->create_course();
234 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
235 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
236
79397b56 237 // Setup the assignment.
757d5b7c
AN
238 $this->setUser($teacher);
239 $time = time();
240 $assign = $this->create_instance($course, [
241 'assignsubmission_onlinetext_enabled' => 1,
242 'duedate' => time() - (4 * DAYSECS),
243 ]);
79397b56
DB
244 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
245 'id' => $assign->get_course_module()->id,
246 'action' => 'grading',
247 )));
248
249 // Check that the assignment is late.
250 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
251 $output = $assign->get_renderer()->render($gradingtable);
252 $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
757d5b7c 253 $this->assertContains(get_string('overdue', 'assign', format_time((4 * DAYSECS))), $output);
79397b56
DB
254
255 // Grant an extension.
757d5b7c
AN
256 $extendedtime = $time + (2 * DAYSECS);
257 $assign->testable_save_user_extension($student->id, $extendedtime);
79397b56
DB
258 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
259 $output = $assign->get_renderer()->render($gradingtable);
260 $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
5b460d65 261 $this->assertContains(get_string('userextensiondate', 'assign', userdate($extendedtime)), $output);
79397b56
DB
262
263 // Simulate a submission.
757d5b7c
AN
264 $this->setUser($student);
265 $submission = $assign->get_user_submission($student->id, true);
79397b56 266 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
757d5b7c 267 $assign->testable_update_submission($submission, $student->id, true, false);
79397b56 268 $data = new stdClass();
757d5b7c
AN
269 $data->onlinetext_editor = [
270 'itemid' => file_get_unused_draft_itemid(),
271 'text' => 'Submission text',
272 'format' => FORMAT_MOODLE,
273 ];
79397b56
DB
274 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
275 $plugin->save($submission, $data);
276
277 // Verify output.
757d5b7c 278 $this->setUser($teacher);
79397b56
DB
279 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
280 $output = $assign->get_renderer()->render($gradingtable);
281 $this->assertContains(get_string('submissionstatus_submitted', 'assign'), $output);
5b460d65 282 $this->assertContains(get_string('userextensiondate', 'assign', userdate($extendedtime)), $output);
79397b56
DB
283 }
284
285 /**
286 * Test that late submissions with extension date calculate correctly.
287 */
288 public function test_gradingtable_extension_date_calculation_for_lateness() {
289 global $PAGE;
290
757d5b7c
AN
291 $this->resetAfterTest();
292 $course = $this->getDataGenerator()->create_course();
293 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
294 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
295
79397b56 296 // Setup the assignment.
757d5b7c 297 $this->setUser($teacher);
a0f8db77 298 $time = time();
757d5b7c
AN
299 $assign = $this->create_instance($course, [
300 'assignsubmission_onlinetext_enabled' => 1,
301 'duedate' => time() - (4 * DAYSECS),
302 ]);
79397b56
DB
303 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
304 'id' => $assign->get_course_module()->id,
305 'action' => 'grading',
306 )));
307
308 // Check that the assignment is late.
309 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
310 $output = $assign->get_renderer()->render($gradingtable);
311 $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
a0f8db77 312 $difftime = time() - $time;
757d5b7c 313 $this->assertContains(get_string('overdue', 'assign', format_time((4 * DAYSECS) + $difftime)), $output);
79397b56
DB
314
315 // Grant an extension that is in the past.
757d5b7c 316 $assign->testable_save_user_extension($student->id, $time - (2 * DAYSECS));
79397b56
DB
317 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
318 $output = $assign->get_renderer()->render($gradingtable);
319 $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
757d5b7c 320 $this->assertContains(get_string('userextensiondate', 'assign', userdate($time - (2 * DAYSECS))), $output);
a0f8db77 321 $difftime = time() - $time;
757d5b7c 322 $this->assertContains(get_string('overdue', 'assign', format_time((2 * DAYSECS) + $difftime)), $output);
79397b56
DB
323
324 // Simulate a submission.
757d5b7c
AN
325 $this->setUser($student);
326 $submission = $assign->get_user_submission($student->id, true);
79397b56 327 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
757d5b7c 328 $assign->testable_update_submission($submission, $student->id, true, false);
79397b56 329 $data = new stdClass();
757d5b7c
AN
330 $data->onlinetext_editor = [
331 'itemid' => file_get_unused_draft_itemid(),
332 'text' => 'Submission text',
333 'format' => FORMAT_MOODLE,
334 ];
79397b56
DB
335 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
336 $plugin->save($submission, $data);
a0f8db77 337 $submittedtime = time();
79397b56
DB
338
339 // Verify output.
757d5b7c 340 $this->setUser($teacher);
79397b56
DB
341 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
342 $output = $assign->get_renderer()->render($gradingtable);
343 $this->assertContains(get_string('submissionstatus_submitted', 'assign'), $output);
757d5b7c 344 $this->assertContains(get_string('userextensiondate', 'assign', userdate($time - (2 * DAYSECS))), $output);
a0f8db77
DW
345
346 $difftime = $submittedtime - $time;
757d5b7c 347 $this->assertContains(get_string('submittedlateshort', 'assign', format_time((2 * DAYSECS) + $difftime)), $output);
79397b56
DB
348 }
349
232b26f0
CS
350 public function test_gradingtable_status_rendering() {
351 global $PAGE;
352
757d5b7c
AN
353 $this->resetAfterTest();
354 $course = $this->getDataGenerator()->create_course();
355 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
356 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
357
232b26f0 358 // Setup the assignment.
757d5b7c 359 $this->setUser($teacher);
232b26f0 360 $time = time();
757d5b7c 361 $assign = $this->create_instance($course, [
232b26f0 362 'assignsubmission_onlinetext_enabled' => 1,
757d5b7c
AN
363 'duedate' => $time - (4 * DAYSECS),
364 ]);
232b26f0
CS
365 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
366 'id' => $assign->get_course_module()->id,
367 'action' => 'grading',
368 )));
369
370 // Check that the assignment is late.
371 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
372 $output = $assign->get_renderer()->render($gradingtable);
373 $this->assertContains(get_string('submissionstatus_', 'assign'), $output);
374 $difftime = time() - $time;
757d5b7c 375 $this->assertContains(get_string('overdue', 'assign', format_time((4 * DAYSECS) + $difftime)), $output);
232b26f0
CS
376
377 // Simulate a student viewing the assignment without submitting.
757d5b7c
AN
378 $this->setUser($student);
379 $submission = $assign->get_user_submission($student->id, true);
232b26f0 380 $submission->status = ASSIGN_SUBMISSION_STATUS_NEW;
757d5b7c 381 $assign->testable_update_submission($submission, $student->id, true, false);
232b26f0
CS
382 $submittedtime = time();
383
384 // Verify output.
757d5b7c 385 $this->setUser($teacher);
232b26f0
CS
386 $gradingtable = new assign_grading_table($assign, 1, '', 0, true);
387 $output = $assign->get_renderer()->render($gradingtable);
388 $difftime = $submittedtime - $time;
757d5b7c 389 $this->assertContains(get_string('overdue', 'assign', format_time((4 * DAYSECS) + $difftime)), $output);
232b26f0
CS
390
391 $document = new DOMDocument();
890c2603 392 @$document->loadHTML($output);
232b26f0
CS
393 $xpath = new DOMXPath($document);
394 $this->assertEquals('', $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c8"])'));
395 }
396
79397b56
DB
397 /**
398 * Check that group submission information is rendered correctly in the
399 * grading table.
400 */
401 public function test_gradingtable_group_submissions_rendering() {
402 global $PAGE;
403
757d5b7c
AN
404 $this->resetAfterTest();
405 $course = $this->getDataGenerator()->create_course();
406 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
407
408 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
409 groups_add_member($group, $teacher);
410
411 $students = [];
412
413 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
414 $students[] = $student;
415 groups_add_member($group, $student);
416
417 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
418 $students[] = $student;
419 groups_add_member($group, $student);
420
421 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
422 $students[] = $student;
423 groups_add_member($group, $student);
424
425 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
426 $students[] = $student;
427 groups_add_member($group, $student);
428
429 // Verify group assignments.
430 $this->setUser($teacher);
431 $assign = $this->create_instance($course, [
79397b56
DB
432 'teamsubmission' => 1,
433 'assignsubmission_onlinetext_enabled' => 1,
434 'submissiondrafts' => 1,
435 'requireallteammemberssubmit' => 0,
757d5b7c 436 ]);
79397b56
DB
437 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array(
438 'id' => $assign->get_course_module()->id,
439 'action' => 'grading',
440 )));
441
442 // Add a submission.
757d5b7c 443 $this->setUser($student);
79397b56 444 $data = new stdClass();
757d5b7c
AN
445 $data->onlinetext_editor = [
446 'itemid' => file_get_unused_draft_itemid(),
447 'text' => 'Submission text',
448 'format' => FORMAT_MOODLE,
449 ];
79397b56
DB
450 $notices = array();
451 $assign->save_submission($data, $notices);
452
757d5b7c 453 $submission = $assign->get_group_submission($student->id, 0, true);
79397b56 454 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
757d5b7c 455 $assign->testable_update_submission($submission, $student->id, true, true);
79397b56
DB
456
457 // Check output.
757d5b7c 458 $this->setUser($teacher);
79397b56
DB
459 $gradingtable = new assign_grading_table($assign, 4, '', 0, true);
460 $output = $assign->get_renderer()->render($gradingtable);
461 $document = new DOMDocument();
890c2603 462 @$document->loadHTML($output);
79397b56
DB
463 $xpath = new DOMXPath($document);
464
465 // Check status.
466 $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c4"]/div[@class="submissionstatussubmitted"])'));
467 $this->assertSame(get_string('submissionstatus_submitted', 'assign'), $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c4"]/div[@class="submissionstatussubmitted"])'));
468
469 // Check submission last modified date
470 $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c8"])')));
471 $this->assertGreaterThan(0, strtotime($xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c8"])')));
472
473 // Check group.
757d5b7c
AN
474 $this->assertSame($group->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c5"])'));
475 $this->assertSame($group->name, $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c5"])'));
79397b56
DB
476
477 // Check submission text.
478 $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r0_c9"]/div/div)'));
479 $this->assertSame('Submission text', $xpath->evaluate('string(//td[@id="mod_assign_grading_r3_c9"]/div/div)'));
480
481 // Check comments can be made.
482 $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r0_c10"]//textarea)'));
483 $this->assertSame(1, (int)$xpath->evaluate('count(//td[@id="mod_assign_grading_r3_c10"]//textarea)'));
484 }
485
47f48152 486 public function test_show_intro() {
757d5b7c
AN
487 $this->resetAfterTest();
488 $course = $this->getDataGenerator()->create_course();
489 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
490
47f48152 491 // Test whether we are showing the intro at the correct times.
757d5b7c
AN
492 $this->setUser($teacher);
493 $assign = $this->create_instance($course, ['alwaysshowdescription' => 1]);
47f48152
DW
494
495 $this->assertEquals(true, $assign->testable_show_intro());
496
757d5b7c 497 $tomorrow = time() + DAYSECS;
47f48152 498
757d5b7c
AN
499 $assign = $this->create_instance($course, [
500 'alwaysshowdescription' => 0,
501 'allowsubmissionsfromdate' => $tomorrow,
502 ]);
47f48152 503 $this->assertEquals(false, $assign->testable_show_intro());
757d5b7c
AN
504 $yesterday = time() - DAYSECS;
505 $assign = $this->create_instance($course, [
506 'alwaysshowdescription' => 0,
507 'allowsubmissionsfromdate' => $yesterday,
508 ]);
47f48152
DW
509 $this->assertEquals(true, $assign->testable_show_intro());
510 }
511
512 public function test_has_submissions_or_grades() {
757d5b7c
AN
513 $this->resetAfterTest();
514 $course = $this->getDataGenerator()->create_course();
515 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
516 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
47f48152 517
757d5b7c
AN
518 $this->setUser($teacher);
519 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
47f48152
DW
520 $instance = $assign->get_instance();
521
522 // Should start empty.
523 $this->assertEquals(false, $assign->has_submissions_or_grades());
524
525 // Simulate a submission.
757d5b7c
AN
526 $this->setUser($student);
527 $submission = $assign->get_user_submission($student->id, true);
ab14ab74
FM
528
529 // The submission is still new.
530 $this->assertEquals(false, $assign->has_submissions_or_grades());
531
532 // Submit the submission.
533 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
757d5b7c 534 $assign->testable_update_submission($submission, $student->id, true, false);
47f48152
DW
535 $data = new stdClass();
536 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
537 'text'=>'Submission text',
538 'format'=>FORMAT_MOODLE);
539 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
540 $plugin->save($submission, $data);
541
542 // Now test again.
543 $this->assertEquals(true, $assign->has_submissions_or_grades());
47f48152
DW
544 }
545
546 public function test_delete_grades() {
757d5b7c
AN
547 $this->resetAfterTest();
548 $course = $this->getDataGenerator()->create_course();
549 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
550 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
551
552 $this->setUser($teacher);
553 $assign = $this->create_instance($course);
47f48152
DW
554
555 // Simulate adding a grade.
757d5b7c 556 $this->setUser($teacher);
47f48152
DW
557 $data = new stdClass();
558 $data->grade = '50.0';
757d5b7c 559 $assign->testable_apply_grade_to_user($data, $student->id, 0);
47f48152
DW
560
561 // Now see if the data is in the gradebook.
757d5b7c 562 $gradinginfo = grade_get_grades($course->id, 'mod', 'assign', $assign->get_instance()->id);
47f48152
DW
563
564 $this->assertNotEquals(0, count($gradinginfo->items));
565
566 $assign->testable_delete_grades();
757d5b7c 567 $gradinginfo = grade_get_grades($course->id, 'mod', 'assign', $assign->get_instance()->id);
47f48152
DW
568
569 $this->assertEquals(0, count($gradinginfo->items));
570 }
571
572 public function test_delete_instance() {
757d5b7c
AN
573 $this->resetAfterTest();
574 $course = $this->getDataGenerator()->create_course();
575 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
576 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
577
578 $this->setUser($teacher);
579 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
47f48152
DW
580
581 // Simulate adding a grade.
757d5b7c 582 $this->setUser($teacher);
47f48152
DW
583 $data = new stdClass();
584 $data->grade = '50.0';
757d5b7c 585 $assign->testable_apply_grade_to_user($data, $student->id, 0);
47f48152
DW
586
587 // Simulate a submission.
757d5b7c 588 $this->add_submission($student, $assign);
47f48152
DW
589
590 // Now try and delete.
757d5b7c 591 $this->setUser($teacher);
47f48152
DW
592 $this->assertEquals(true, $assign->delete_instance());
593 }
594
595 public function test_reset_userdata() {
596 global $DB;
597
757d5b7c
AN
598 $this->resetAfterTest();
599 $course = $this->getDataGenerator()->create_course();
600 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
601 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
602
47f48152 603 $now = time();
757d5b7c
AN
604 $this->setUser($teacher);
605 $assign = $this->create_instance($course, [
606 'assignsubmission_onlinetext_enabled' => 1,
607 'duedate' => $now,
608 ]);
47f48152
DW
609
610 // Simulate adding a grade.
757d5b7c
AN
611 $this->add_submission($student, $assign);
612 $this->submit_for_grading($student, $assign);
613 $this->mark_submission($teacher, $assign, $student, 50.0);
47f48152
DW
614
615 // Simulate a submission.
757d5b7c
AN
616 $this->setUser($student);
617 $submission = $assign->get_user_submission($student->id, true);
47f48152
DW
618 $data = new stdClass();
619 $data->onlinetext_editor = array('itemid'=>file_get_unused_draft_itemid(),
620 'text'=>'Submission text',
621 'format'=>FORMAT_MOODLE);
622 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
623 $plugin->save($submission, $data);
624
625 $this->assertEquals(true, $assign->has_submissions_or_grades());
626 // Now try and reset.
627 $data = new stdClass();
628 $data->reset_assign_submissions = 1;
629 $data->reset_gradebook_grades = 1;
d685b959
IT
630 $data->reset_assign_user_overrides = 1;
631 $data->reset_assign_group_overrides = 1;
757d5b7c
AN
632 $data->courseid = $course->id;
633 $data->timeshift = DAYSECS;
634 $this->setUser($teacher);
47f48152
DW
635 $assign->reset_userdata($data);
636 $this->assertEquals(false, $assign->has_submissions_or_grades());
637
638 // Reload the instance data.
639 $instance = $DB->get_record('assign', array('id'=>$assign->get_instance()->id));
757d5b7c 640 $this->assertEquals($now + DAYSECS, $instance->duedate);
e63515ba
RT
641
642 // Test reset using assign_reset_userdata().
643 $assignduedate = $instance->duedate; // Keep old updated value for comparison.
757d5b7c 644 $data->timeshift = (2 * DAYSECS);
e63515ba
RT
645 assign_reset_userdata($data);
646 $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
757d5b7c 647 $this->assertEquals($assignduedate + (2 * DAYSECS), $instance->duedate);
e63515ba
RT
648
649 // Create one more assignment and reset, make sure time shifted for previous assignment is not changed.
757d5b7c
AN
650 $assign2 = $this->create_instance($course, [
651 'assignsubmission_onlinetext_enabled' => 1,
652 'duedate' => $now,
653 ]);
e63515ba 654 $assignduedate = $instance->duedate;
757d5b7c 655 $data->timeshift = 3*DAYSECS;
e63515ba
RT
656 $assign2->reset_userdata($data);
657 $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
658 $this->assertEquals($assignduedate, $instance->duedate);
659 $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
757d5b7c 660 $this->assertEquals($now + 3*DAYSECS, $instance2->duedate);
e63515ba
RT
661
662 // Reset both assignments using assign_reset_userdata() and make sure both assignments have same date.
663 $assignduedate = $instance->duedate;
664 $assign2duedate = $instance2->duedate;
757d5b7c 665 $data->timeshift = (4 * DAYSECS);
e63515ba
RT
666 assign_reset_userdata($data);
667 $instance = $DB->get_record('assign', array('id' => $assign->get_instance()->id));
757d5b7c 668 $this->assertEquals($assignduedate + (4 * DAYSECS), $instance->duedate);
e63515ba 669 $instance2 = $DB->get_record('assign', array('id' => $assign2->get_instance()->id));
757d5b7c 670 $this->assertEquals($assign2duedate + (4 * DAYSECS), $instance2->duedate);
47f48152
DW
671 }
672
673 public function test_plugin_settings() {
674 global $DB;
675
757d5b7c
AN
676 $this->resetAfterTest();
677
678 $course = $this->getDataGenerator()->create_course();
679 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
680
47f48152 681 $now = time();
757d5b7c
AN
682 $this->setUser($teacher);
683 $assign = $this->create_instance($course, [
684 'assignsubmission_file_enabled' => 1,
685 'assignsubmission_file_maxfiles' => 12,
686 'assignsubmission_file_maxsizebytes' => 10,
687 ]);
47f48152
DW
688
689 $plugin = $assign->get_submission_plugin_by_type('file');
690 $this->assertEquals('12', $plugin->get_config('maxfilesubmissions'));
691 }
692
693 public function test_update_calendar() {
694 global $DB;
695
757d5b7c
AN
696 $this->resetAfterTest();
697
698 $course = $this->getDataGenerator()->create_course();
699 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
700
701 $this->setUser($teacher);
702 $userctx = context_user::instance($teacher->id)->id;
f588ea50
FM
703
704 // Hack to pretend that there was an editor involved. We need both $_POST and $_REQUEST, and a sesskey.
705 $draftid = file_get_unused_draft_itemid();
706 $_REQUEST['introeditor'] = $draftid;
707 $_POST['introeditor'] = $draftid;
708 $_POST['sesskey'] = sesskey();
709
710 // Write links to a draft area.
711 $fakearealink1 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">link</a>', 'draftfile.php', $userctx,
712 'user', 'draft', $draftid);
713 $fakearealink2 = file_rewrite_pluginfile_urls('<a href="@@PLUGINFILE@@/pic.gif">new</a>', 'draftfile.php', $userctx,
714 'user', 'draft', $draftid);
715
716 // Create a new assignment with links to a draft area.
717 $now = time();
757d5b7c
AN
718 $assign = $this->create_instance($course, [
719 'duedate' => $now,
720 'intro' => $fakearealink1,
721 'introformat' => FORMAT_HTML
722 ]);
47f48152
DW
723
724 // See if there is an event in the calendar.
725 $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
f588ea50
FM
726 $event = $DB->get_record('event', $params);
727 $this->assertNotEmpty($event);
728 $this->assertSame('link', $event->description); // The pluginfile links are removed.
47f48152 729
f588ea50
FM
730 // Make sure the same works when updating the assignment.
731 $instance = $assign->get_instance();
732 $instance->instance = $instance->id;
733 $instance->intro = $fakearealink2;
734 $instance->introformat = FORMAT_HTML;
735 $assign->update_instance($instance);
736 $params = array('modulename' => 'assign', 'instance' => $assign->get_instance()->id);
737 $event = $DB->get_record('event', $params);
738 $this->assertNotEmpty($event);
739 $this->assertSame('new', $event->description); // The pluginfile links are removed.
ae7638f7
DW
740
741 // Create an assignment with a description that should be hidden.
757d5b7c
AN
742 $assign = $this->create_instance($course, [
743 'duedate' => $now + 160,
744 'alwaysshowdescription' => false,
745 'allowsubmissionsfromdate' => $now + 60,
746 'intro' => 'Some text',
747 ]);
ae7638f7
DW
748
749 // Get the event from the calendar.
750 $params = array('modulename'=>'assign', 'instance'=>$assign->get_instance()->id);
757d5b7c
AN
751 $event = $DB->get_record('event', [
752 'modulename' => 'assign',
753 'instance' => $assign->get_instance()->id,
754 ]);
ae7638f7
DW
755
756 $this->assertEmpty($event->description);
8112670d
DW
757
758 // Change the allowsubmissionfromdate to the past - do this directly in the DB
759 // because if we call the assignment update method - it will update the calendar
760 // and we want to test that this works from cron.
761 $DB->set_field('assign', 'allowsubmissionsfromdate', $now - 60, array('id'=>$assign->get_instance()->id));
ae7638f7
DW
762 // Run cron to update the event in the calendar.
763 assign::cron();
764 $event = $DB->get_record('event', $params);
765
766 $this->assertContains('Some text', $event->description);
767
47f48152
DW
768 }
769
770 public function test_update_instance() {
771 global $DB;
772
757d5b7c
AN
773 $this->resetAfterTest();
774
775 $course = $this->getDataGenerator()->create_course();
776 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
777
778 $this->setUser($teacher);
779 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
47f48152
DW
780
781 $now = time();
782 $instance = $assign->get_instance();
783 $instance->duedate = $now;
784 $instance->instance = $instance->id;
785 $instance->assignsubmission_onlinetext_enabled = 1;
47f48152
DW
786
787 $assign->update_instance($instance);
788
757d5b7c 789 $instance = $DB->get_record('assign', ['id' => $assign->get_instance()->id]);
47f48152
DW
790 $this->assertEquals($now, $instance->duedate);
791 }
792
8fd00a24
DW
793 public function test_cannot_submit_empty() {
794 global $PAGE;
795
757d5b7c
AN
796 $this->resetAfterTest();
797
798 $course = $this->getDataGenerator()->create_course();
799 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
800
801 $assign = $this->create_instance($course, ['submissiondrafts' => 1]);
8fd00a24 802
757d5b7c 803 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
8fd00a24
DW
804
805 // Test you cannot see the submit button for an offline assignment regardless.
757d5b7c
AN
806 $this->setUser($student);
807 $output = $assign->view_student_summary($student, true);
8fd00a24 808 $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Can submit empty offline assignment');
757d5b7c 809 }
8fd00a24 810
757d5b7c
AN
811 public function test_cannot_submit_empty_no_submission() {
812 global $PAGE;
8fd00a24 813
757d5b7c
AN
814 $this->resetAfterTest();
815
816 $course = $this->getDataGenerator()->create_course();
817 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
818
819 $assign = $this->create_instance($course, [
820 'submissiondrafts' => 1,
821 'assignsubmission_onlinetext_enabled' => 1,
822 ]);
823
824 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
825
826 // Test you cannot see the submit button for an online text assignment with no submission.
827 $this->setUser($student);
828 $output = $assign->view_student_summary($student, true);
8fd00a24 829 $this->assertNotContains(get_string('submitassignment', 'assign'), $output, 'Cannot submit empty onlinetext assignment');
757d5b7c
AN
830 }
831
832 public function test_can_submit_with_submission() {
833 global $PAGE;
834
835 $this->resetAfterTest();
836
837 $course = $this->getDataGenerator()->create_course();
838 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
839
840 $assign = $this->create_instance($course, [
841 'submissiondrafts' => 1,
842 'assignsubmission_onlinetext_enabled' => 1,
843 ]);
844
845 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
846
847 // Add a draft.
848 $this->add_submission($student, $assign);
8fd00a24 849
8fd00a24 850 // Test you can see the submit button for an online text assignment with a submission.
757d5b7c
AN
851 $this->setUser($student);
852 $output = $assign->view_student_summary($student, true);
8fd00a24
DW
853 $this->assertContains(get_string('submitassignment', 'assign'), $output, 'Can submit non empty onlinetext assignment');
854 }
855
c89d23ee
CB
856 /**
857 * Test new_submission_empty
858 *
859 * We only test combinations of plugins here. Individual plugins are tested
860 * in their respective test files.
861 *
862 * @dataProvider test_new_submission_empty_testcases
863 * @param string $data The file submission data
864 * @param bool $expected The expected return value
865 */
866 public function test_new_submission_empty($data, $expected) {
867 $this->resetAfterTest();
757d5b7c
AN
868
869 $course = $this->getDataGenerator()->create_course();
870 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
871
872 $assign = $this->create_instance($course, [
873 'assignsubmission_file_enabled' => 1,
874 'assignsubmission_file_maxfiles' => 12,
875 'assignsubmission_file_maxsizebytes' => 10,
876 'assignsubmission_onlinetext_enabled' => 1,
877 ]);
878 $this->setUser($student);
c89d23ee
CB
879 $submission = new stdClass();
880
881 if ($data['file'] && isset($data['file']['filename'])) {
882 $itemid = file_get_unused_draft_itemid();
883 $submission->files_filemanager = $itemid;
757d5b7c 884 $data['file'] += ['contextid' => context_user::instance($student->id)->id, 'itemid' => $itemid];
c89d23ee
CB
885 $fs = get_file_storage();
886 $fs->create_file_from_string((object)$data['file'], 'Content of ' . $data['file']['filename']);
887 }
888
889 if ($data['onlinetext']) {
890 $submission->onlinetext_editor = ['text' => $data['onlinetext']];
891 }
892
893 $result = $assign->new_submission_empty($submission);
894 $this->assertTrue($result === $expected);
895 }
896
897 /**
898 * Dataprovider for the test_new_submission_empty testcase
899 *
900 * @return array of testcases
901 */
902 public function test_new_submission_empty_testcases() {
903 return [
904 'With file and onlinetext' => [
905 [
906 'file' => [
907 'component' => 'user',
908 'filearea' => 'draft',
909 'filepath' => '/',
910 'filename' => 'not_a_virus.exe'
911 ],
912 'onlinetext' => 'Balin Fundinul Uzbadkhazaddumu'
913 ],
914 false
915 ]
916 ];
917 }
918
47f48152 919 public function test_list_participants() {
757d5b7c
AN
920 global $CFG;
921
922 $this->resetAfterTest();
923
924 $course = $this->getDataGenerator()->create_course();
925 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
926
927 // Create 10 students.
928 for ($i = 0; $i < 10; $i++) {
929 $this->getDataGenerator()->create_and_enrol($course, 'student');
930 }
931
932 $this->setUser($teacher);
933 $assign = $this->create_instance($course, ['grade' => 100]);
934
935 $this->assertCount(10, $assign->list_participants(null, true));
936 }
937
938 public function test_list_participants_activeenrol() {
c13ac85d 939 global $CFG, $DB;
940
757d5b7c 941 $this->resetAfterTest();
47f48152 942
757d5b7c
AN
943 $course = $this->getDataGenerator()->create_course();
944 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
e6cc5347 945
757d5b7c
AN
946 // Create 10 students.
947 for ($i = 0; $i < 10; $i++) {
948 $this->getDataGenerator()->create_and_enrol($course, 'student');
949 }
e6cc5347 950
757d5b7c
AN
951 // Create 10 suspended students.
952 for ($i = 0; $i < 10; $i++) {
953 $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
954 }
955
956 $this->setUser($teacher);
e6cc5347 957 set_user_preference('grade_report_showonlyactiveenrol', false);
757d5b7c
AN
958 $assign = $this->create_instance($course, ['grade' => 100]);
959
960 $this->assertCount(10, $assign->list_participants(null, true));
961 }
962
963 public function test_list_participants_with_group_restriction() {
964 global $CFG;
965
966 $this->resetAfterTest();
967
968 $course = $this->getDataGenerator()->create_course();
969 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
970 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
971 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
972 $unrelatedstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
c13ac85d 973
757d5b7c 974 // Turn on availability and a group restriction, and check that it doesn't show users who aren't in the group.
c13ac85d 975 $CFG->enableavailability = true;
757d5b7c
AN
976
977 $specialgroup = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
978 $assign = $this->create_instance($course, [
979 'grade' => 100,
980 'availability' => json_encode(
981 \core_availability\tree::get_root_json([\availability_group\condition::get_json($specialgroup->id)])
982 ),
983 ]);
984
985 groups_add_member($specialgroup, $student);
986 groups_add_member($specialgroup, $otherstudent);
c13ac85d 987 $this->assertEquals(2, count($assign->list_participants(null, true)));
47f48152
DW
988 }
989
1b2f9dc6 990 public function test_get_participant_user_not_exist() {
757d5b7c
AN
991 $this->resetAfterTest();
992 $course = $this->getDataGenerator()->create_course();
993
994 $assign = $this->create_instance($course);
1b2f9dc6
RW
995 $this->assertNull($assign->get_participant('-1'));
996 }
997
998 public function test_get_participant_not_enrolled() {
757d5b7c
AN
999 $this->resetAfterTest();
1000 $course = $this->getDataGenerator()->create_course();
1001 $assign = $this->create_instance($course);
1002
1b2f9dc6
RW
1003 $user = $this->getDataGenerator()->create_user();
1004 $this->assertNull($assign->get_participant($user->id));
1005 }
1006
1007 public function test_get_participant_no_submission() {
757d5b7c
AN
1008 $this->resetAfterTest();
1009 $course = $this->getDataGenerator()->create_course();
1010 $assign = $this->create_instance($course);
1011 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1012
1b2f9dc6
RW
1013 $participant = $assign->get_participant($student->id);
1014
1015 $this->assertEquals($student->id, $participant->id);
1016 $this->assertFalse($participant->submitted);
1017 $this->assertFalse($participant->requiregrading);
eb43ef6c
TB
1018 $this->assertFalse($participant->grantedextension);
1019 }
1020
1021 public function test_get_participant_granted_extension() {
757d5b7c
AN
1022 $this->resetAfterTest();
1023 $course = $this->getDataGenerator()->create_course();
1024 $assign = $this->create_instance($course);
1025 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1026 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1027
1028 $this->setUser($teacher);
eb43ef6c
TB
1029 $assign->save_user_extension($student->id, time());
1030 $participant = $assign->get_participant($student->id);
1031
1032 $this->assertEquals($student->id, $participant->id);
1033 $this->assertFalse($participant->submitted);
1034 $this->assertFalse($participant->requiregrading);
1035 $this->assertTrue($participant->grantedextension);
1b2f9dc6
RW
1036 }
1037
1038 public function test_get_participant_with_ungraded_submission() {
757d5b7c
AN
1039 $this->resetAfterTest();
1040 $course = $this->getDataGenerator()->create_course();
1041 $assign = $this->create_instance($course);
1042 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1043 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1b2f9dc6
RW
1044
1045 // Simulate a submission.
757d5b7c
AN
1046 $this->add_submission($student, $assign);
1047 $this->submit_for_grading($student, $assign);
1b2f9dc6
RW
1048
1049 $participant = $assign->get_participant($student->id);
1050
1051 $this->assertEquals($student->id, $participant->id);
1052 $this->assertTrue($participant->submitted);
1053 $this->assertTrue($participant->requiregrading);
eb43ef6c 1054 $this->assertFalse($participant->grantedextension);
1b2f9dc6
RW
1055 }
1056
1057 public function test_get_participant_with_graded_submission() {
757d5b7c
AN
1058 $this->resetAfterTest();
1059 $course = $this->getDataGenerator()->create_course();
1060 $assign = $this->create_instance($course);
1061 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1062 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1b2f9dc6
RW
1063
1064 // Simulate a submission.
757d5b7c
AN
1065 $this->add_submission($student, $assign);
1066 $this->submit_for_grading($student, $assign);
1b2f9dc6 1067
757d5b7c 1068 $this->mark_submission($teacher, $assign, $student, 50.0);
1b2f9dc6
RW
1069
1070 $data = new stdClass();
1071 $data->grade = '50.0';
1072 $assign->testable_apply_grade_to_user($data, $student->id, 0);
1073
1074 $participant = $assign->get_participant($student->id);
1075
1076 $this->assertEquals($student->id, $participant->id);
1077 $this->assertTrue($participant->submitted);
1078 $this->assertFalse($participant->requiregrading);
eb43ef6c 1079 $this->assertFalse($participant->grantedextension);
1b2f9dc6
RW
1080 }
1081
757d5b7c
AN
1082 /**
1083 * No active group and non-group submissions disallowed => 2 groups.
1084 */
1085 public function test_count_teams_no_active_non_group_allowed() {
1086 $this->resetAfterTest();
2dda9417 1087
757d5b7c
AN
1088 $course = $this->getDataGenerator()->create_course();
1089 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1090 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1091 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
2dda9417 1092
757d5b7c
AN
1093 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
1094 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1095 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1096 groups_add_member($group1, $student1);
1097 groups_add_member($group2, $student2);
e528997a 1098
757d5b7c
AN
1099 $this->setUser($teacher);
1100 $assign = $this->create_instance($course, ['teamsubmission' => 1]);
e528997a 1101
757d5b7c 1102 $this->assertEquals(2, $assign->count_teams());
47f48152
DW
1103 }
1104
757d5b7c
AN
1105 /**
1106 * No active group and non group submissions allowed => 2 groups + the default one.
1107 */
1108 public function test_count_teams_non_group_allowed() {
1109 $this->resetAfterTest();
e2d2d8a1 1110
757d5b7c
AN
1111 $course = $this->getDataGenerator()->create_course();
1112 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1113 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1114 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1115 $student3 = $this->getDataGenerator()->create_and_enrol($course, 'student');
e2d2d8a1 1116
757d5b7c
AN
1117 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
1118 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
e2d2d8a1 1119
757d5b7c
AN
1120 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id, 'groupingid' => $grouping->id));
1121 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1122 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group2->id, 'groupingid' => $grouping->id));
1123
1124 groups_add_member($group1, $student1);
1125 groups_add_member($group2, $student2);
1126
1127 $assign = $this->create_instance($course, [
1128 'teamsubmission' => 1,
1129 'teamsubmissiongroupingid' => $grouping->id,
1130 'preventsubmissionnotingroup' => false,
1131 ]);
1132
1133 $this->setUser($teacher);
1134 $this->assertEquals(3, $assign->count_teams());
1135
1136 // Active group only.
1137 $this->assertEquals(1, $assign->count_teams($group1->id));
1138 $this->assertEquals(1, $assign->count_teams($group2->id));
1139 }
1140
1141 /**
1142 * Active group => just selected one.
1143 */
1144 public function test_count_teams_no_active_group() {
1145 $this->resetAfterTest();
1146
1147 $course = $this->getDataGenerator()->create_course();
1148 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1149 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1150 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1151 $student3 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1152
1153 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
1154 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1155
1156 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id, 'groupingid' => $grouping->id));
1157 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1158 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group2->id, 'groupingid' => $grouping->id));
1159
1160 groups_add_member($group1, $student1);
1161 groups_add_member($group2, $student2);
1162
1163 $assign = $this->create_instance($course, [
1164 'teamsubmission' => 1,
1165 'preventsubmissionnotingroup' => true,
1166 ]);
1167
1168 $this->setUser($teacher);
1169 $this->assertEquals(2, $assign->count_teams());
1170
1171 // Active group only.
1172 $this->assertEquals(1, $assign->count_teams($group1->id));
1173 $this->assertEquals(1, $assign->count_teams($group2->id));
1174 }
1175
1176 /**
1177 * Active group => just selected one.
1178 */
1179 public function test_count_teams_groups_only() {
1180 $this->resetAfterTest();
1181
1182 $course = $this->getDataGenerator()->create_course();
1183 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
1184
1185 $assign = $this->create_instance($course, [
1186 'teamsubmission' => 1,
1187 'teamsubmissiongroupingid' => $grouping->id,
1188 'preventsubmissionnotingroup' => false,
1189 ]);
1190 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1191
1192 $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1193 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1194 groups_add_member($group1, $student1);
1195
1196 $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
1197 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1198 groups_add_member($group2, $student2);
1199
1200 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id, 'groupingid' => $grouping->id));
1201 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group2->id, 'groupingid' => $grouping->id));
1202
1203 $this->setUser($teacher);
1204
1205 $assign = $this->create_instance($course, [
1206 'teamsubmission' => 1,
1207 'preventsubmissionnotingroup' => true,
1208 ]);
1209 $this->assertEquals(2, $assign->count_teams());
1210 }
1211
1212 public function test_submit_to_default_group() {
1213 global $DB, $SESSION;
1214
1215 $this->resetAfterTest();
1216
1217 $course = $this->getDataGenerator()->create_course();
1218 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1219 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1220
1221 $grouping = $this->getDataGenerator()->create_grouping(['courseid' => $course->id]);
1222 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1223
1224 $assign = $this->create_instance($course, [
1225 'teamsubmission' => 1,
1226 'assignsubmission_onlinetext_enabled' => 1,
1227 'submissiondrafts' => 0,
1228 'groupmode' => VISIBLEGROUPS,
1229 ]);
1230
1231 $usergroup = $assign->get_submission_group($student->id);
1232 $this->assertFalse($usergroup, 'New student is in default group');
1233
1234 // Add a submission.
1235 $this->add_submission($student, $assign);
1236 $this->submit_for_grading($student, $assign);
e2d2d8a1 1237
2dda9417 1238 // Set active groups to all groups.
757d5b7c
AN
1239 $this->setUser($teacher);
1240 $SESSION->activegroup[$course->id]['aag'][0] = 0;
2dda9417
DM
1241 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1242
1243 // Set an active group.
757d5b7c 1244 $SESSION->activegroup[$course->id]['aag'][0] = (int) $group->id;
2dda9417 1245 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
757d5b7c 1246 }
2dda9417 1247
757d5b7c
AN
1248 public function test_count_submissions_no_draft() {
1249 $this->resetAfterTest();
1250
1251 $course = $this->getDataGenerator()->create_course();
1252 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1253 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1254
1255 $assign = $this->create_instance($course, [
1256 'assignsubmission_onlinetext_enabled' => 1,
1257 ]);
1258
1259 $assign->get_user_submission($student->id, true);
1260
1261 // Note: Drafts count as a submission.
1262 $this->assertEquals(0, $assign->count_grades());
1263 $this->assertEquals(0, $assign->count_submissions());
1264 $this->assertEquals(1, $assign->count_submissions(true));
1265 $this->assertEquals(0, $assign->count_submissions_need_grading());
1266 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_NEW));
1267 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
1268 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1269 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_REOPENED));
1270 }
1271
1272 public function test_count_submissions_draft() {
1273 $this->resetAfterTest();
1274
1275 $course = $this->getDataGenerator()->create_course();
1276 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1277 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1278
1279 $assign = $this->create_instance($course, [
1280 'assignsubmission_onlinetext_enabled' => 1,
1281 ]);
1282
1283 $this->add_submission($student, $assign);
1284
1285 // Note: Drafts count as a submission.
1286 $this->assertEquals(0, $assign->count_grades());
1287 $this->assertEquals(1, $assign->count_submissions());
1288 $this->assertEquals(1, $assign->count_submissions(true));
1289 $this->assertEquals(0, $assign->count_submissions_need_grading());
1290 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_NEW));
1291 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
1292 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1293 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_REOPENED));
e2d2d8a1
DW
1294 }
1295
757d5b7c 1296 public function test_count_submissions_submitted() {
2dda9417
DM
1297 global $SESSION;
1298
757d5b7c 1299 $this->resetAfterTest();
47f48152 1300
757d5b7c
AN
1301 $course = $this->getDataGenerator()->create_course();
1302 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1303 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
47f48152 1304
757d5b7c
AN
1305 $assign = $this->create_instance($course, [
1306 'assignsubmission_onlinetext_enabled' => 1,
1307 ]);
47f48152 1308
757d5b7c
AN
1309 $this->add_submission($student, $assign);
1310 $this->submit_for_grading($student, $assign);
47f48152 1311
757d5b7c
AN
1312 $this->assertEquals(0, $assign->count_grades());
1313 $this->assertEquals(1, $assign->count_submissions());
1314 $this->assertEquals(1, $assign->count_submissions(true));
1315 $this->assertEquals(1, $assign->count_submissions_need_grading());
1316 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_NEW));
1317 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
1318 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1319 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_REOPENED));
1320 }
47f48152 1321
757d5b7c
AN
1322 public function test_count_submissions_graded() {
1323 $this->resetAfterTest();
47f48152 1324
757d5b7c
AN
1325 $course = $this->getDataGenerator()->create_course();
1326 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1327 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
e6cc5347 1328
757d5b7c
AN
1329 $assign = $this->create_instance($course, [
1330 'assignsubmission_onlinetext_enabled' => 1,
1331 ]);
1332
1333 $this->add_submission($student, $assign);
1334 $this->submit_for_grading($student, $assign);
757d5b7c 1335 $this->mark_submission($teacher, $assign, $student, 50.0);
47f48152 1336
757d5b7c
AN
1337 // Although it has been graded, it is still marked as submitted.
1338 $this->assertEquals(1, $assign->count_grades());
1339 $this->assertEquals(1, $assign->count_submissions());
1340 $this->assertEquals(1, $assign->count_submissions(true));
1341 $this->assertEquals(0, $assign->count_submissions_need_grading());
1342 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_NEW));
1343 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
1344 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1345 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_REOPENED));
1346 }
2dda9417 1347
757d5b7c
AN
1348 public function test_count_submissions_graded_group() {
1349 global $SESSION;
2dda9417 1350
757d5b7c 1351 $this->resetAfterTest();
2dda9417 1352
757d5b7c
AN
1353 $course = $this->getDataGenerator()->create_course();
1354 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1355 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1356 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1357 $othergroup = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1358 groups_add_member($group, $student);
2dda9417 1359
757d5b7c
AN
1360 $assign = $this->create_instance($course, [
1361 'assignsubmission_onlinetext_enabled' => 1,
1362 'groupmode' => VISIBLEGROUPS,
1363 ]);
1364
1365 $this->add_submission($student, $assign);
1366 $this->submit_for_grading($student, $assign);
1367
1368 // The user should still be listed when fetching all groups.
1369 $this->setUser($teacher);
1370 $SESSION->activegroup[$course->id]['aag'][0] = 0;
1371 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1372
1373 // The user should still be listed when fetching just their group.
1374 $SESSION->activegroup[$course->id]['aag'][0] = $group->id;
1375 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1376
1377 // The user should still be listed when fetching just their group.
1378 $SESSION->activegroup[$course->id]['aag'][0] = $othergroup->id;
1379 $this->assertEquals(0, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1380 }
1381
1382 // TODO
1383 public function x_test_count_submissions_for_team() {
1384 $this->resetAfterTest();
1385
1386 $course = $this->getDataGenerator()->create_course();
1387 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1388 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1389 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1390 $othergroup = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1391 groups_add_member($group, $student);
1392
1393 $assign = $this->create_instance($course, [
1394 'assignsubmission_onlinetext_enabled' => 1,
1395 'teamsubmission' => 1,
1396 ]);
1397
1398 // Add a graded submission.
1399 $this->add_submission($student, $assign);
2dda9417 1400
ab14ab74 1401
ab14ab74
FM
1402
1403 // Simulate adding a grade.
757d5b7c 1404 $this->setUser($teacher);
ab14ab74
FM
1405 $data = new stdClass();
1406 $data->grade = '50.0';
1407 $assign->testable_apply_grade_to_user($data, $this->extrastudents[0]->id, 0);
1408
1409 // Simulate a submission.
1410 $this->setUser($this->extrastudents[1]);
1411 $submission = $assign->get_group_submission($this->extrastudents[1]->id, $groupid, true);
1412 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1413 $assign->testable_update_submission($submission, $this->extrastudents[1]->id, true, false);
1414 $data = new stdClass();
1415 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1416 'text' => 'Submission text',
1417 'format' => FORMAT_MOODLE);
1418 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1419 $plugin->save($submission, $data);
1420
1421 // Simulate a submission.
1422 $this->setUser($this->extrastudents[2]);
1423 $submission = $assign->get_group_submission($this->extrastudents[2]->id, $groupid, true);
1424 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1425 $assign->testable_update_submission($submission, $this->extrastudents[2]->id, true, false);
1426 $data = new stdClass();
1427 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1428 'text' => 'Submission text',
1429 'format' => FORMAT_MOODLE);
1430 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1431 $plugin->save($submission, $data);
1432
1433 // Simulate a submission.
1434 $this->setUser($this->extrastudents[3]);
1435 $submission = $assign->get_group_submission($this->extrastudents[3]->id, $groupid, true);
1436 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
1437 $assign->testable_update_submission($submission, $this->extrastudents[3]->id, true, false);
1438 $data = new stdClass();
1439 $data->onlinetext_editor = array('itemid' => file_get_unused_draft_itemid(),
1440 'text' => 'Submission text',
1441 'format' => FORMAT_MOODLE);
1442 $plugin = $assign->get_submission_plugin_by_type('onlinetext');
1443 $plugin->save($submission, $data);
1444
1445 // Simulate adding a grade.
757d5b7c 1446 $this->setUser($teacher);
ab14ab74
FM
1447 $data = new stdClass();
1448 $data->grade = '50.0';
1449 $assign->testable_apply_grade_to_user($data, $this->extrastudents[3]->id, 0);
1450 $assign->testable_apply_grade_to_user($data, $this->extrasuspendedstudents[0]->id, 0);
1451
1452 // Create a new submission with status NEW.
1453 $this->setUser($this->extrastudents[4]);
1454 $submission = $assign->get_group_submission($this->extrastudents[4]->id, $groupid, true);
1455
1456 $this->assertEquals(2, $assign->count_grades());
1457 $this->assertEquals(4, $assign->count_submissions());
1458 $this->assertEquals(5, $assign->count_submissions(true));
1459 $this->assertEquals(3, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_SUBMITTED));
1460 $this->assertEquals(1, $assign->count_submissions_with_status(ASSIGN_SUBMISSION_STATUS_DRAFT));
1461 }
1462
757d5b7c
AN
1463 public function test_get_grading_userid_list_only_active() {
1464 $this->resetAfterTest();
1465
1466 $course = $this->getDataGenerator()->create_course();
1467 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1468 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1469 $suspendedstudent = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
1470
1471 $this->setUser($teacher);
1472
1473 $assign = $this->create_instance($course);
1474 $this->assertCount(1, $assign->testable_get_grading_userid_list());
1475 }
1476
1477 public function test_get_grading_userid_list_all() {
1478 $this->resetAfterTest();
47f48152 1479
757d5b7c
AN
1480 $course = $this->getDataGenerator()->create_course();
1481 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1482 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1483 $suspendedstudent = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
e6cc5347 1484
757d5b7c 1485 $this->setUser($teacher);
e6cc5347 1486 set_user_preference('grade_report_showonlyactiveenrol', false);
e6cc5347 1487
757d5b7c
AN
1488 $assign = $this->create_instance($course);
1489 $this->assertCount(2, $assign->testable_get_grading_userid_list());
47f48152
DW
1490 }
1491
1492 public function test_cron() {
36fa0ec9 1493 global $PAGE;
757d5b7c
AN
1494 $this->resetAfterTest();
1495
47f48152
DW
1496 // First run cron so there are no messages waiting to be sent (from other tests).
1497 cron_setup_user();
1498 assign::cron();
1499
757d5b7c
AN
1500 $course = $this->getDataGenerator()->create_course();
1501 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1502 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
47f48152 1503
757d5b7c
AN
1504 // Now create an assignment and add some feedback.
1505 $this->setUser($teacher);
1506 $assign = $this->create_instance($course, [
1507 'sendstudentnotifications' => 1,
1508 ]);
47f48152 1509
757d5b7c
AN
1510 $this->add_submission($student, $assign);
1511 $this->submit_for_grading($student, $assign);
1512 $this->mark_submission($teacher, $assign, $student, 50.0);
f7dc9871 1513
757d5b7c 1514 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
47f48152 1515 cron_setup_user();
757d5b7c 1516 $sink = $this->redirectMessages();
47f48152 1517 assign::cron();
47f48152 1518 $messages = $sink->get_messages();
757d5b7c
AN
1519
1520 $this->assertEquals(1, count($messages));
47f48152
DW
1521 $this->assertEquals(1, $messages[0]->notification);
1522 $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
36fa0ec9 1523 // Test customdata.
2356454f 1524 $customdata = json_decode($messages[0]->customdata);
36fa0ec9
JL
1525 $this->assertEquals($assign->get_course_module()->id, $customdata->cmid);
1526 $this->assertEquals($assign->get_instance()->id, $customdata->instance);
1527 $this->assertEquals('feedbackavailable', $customdata->messagetype);
1528 $userpicture = new user_picture($teacher);
1529 $this->assertEquals($userpicture->get_url($PAGE)->out(false), $customdata->notificationiconurl);
1530 $this->assertEquals(0, $customdata->uniqueidforuser); // Not used in this case.
1531 $this->assertFalse($customdata->blindmarking);
757d5b7c 1532 }
a4b10a52 1533
757d5b7c
AN
1534 public function test_cron_without_notifications() {
1535 $this->resetAfterTest();
1536
1537 // First run cron so there are no messages waiting to be sent (from other tests).
1538 cron_setup_user();
1539 assign::cron();
1540
1541 $course = $this->getDataGenerator()->create_course();
1542 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1543 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1544
1545 // Now create an assignment and add some feedback.
1546 $this->setUser($teacher);
1547 $assign = $this->create_instance($course, [
1548 'sendstudentnotifications' => 1,
1549 ]);
1550
1551 $this->add_submission($student, $assign);
1552 $this->submit_for_grading($student, $assign);
1553 $this->mark_submission($teacher, $assign, $student, 50.0, [
1554 'sendstudentnotifications' => 0,
1555 ]);
1556
1557 cron_setup_user();
1558 $sink = $this->redirectMessages();
a4b10a52
ZD
1559 assign::cron();
1560 $messages = $sink->get_messages();
757d5b7c
AN
1561
1562 $this->assertEquals(0, count($messages));
1563 }
1564
1565 public function test_cron_regraded() {
1566 $this->resetAfterTest();
1567
1568 // First run cron so there are no messages waiting to be sent (from other tests).
1569 cron_setup_user();
1570 assign::cron();
1571
1572 $course = $this->getDataGenerator()->create_course();
1573 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1574 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1575
1576 // Now create an assignment and add some feedback.
1577 $this->setUser($teacher);
1578 $assign = $this->create_instance($course, [
1579 'sendstudentnotifications' => 1,
1580 ]);
1581
1582 $this->add_submission($student, $assign);
1583 $this->submit_for_grading($student, $assign);
1584 $this->mark_submission($teacher, $assign, $student, 50.0);
1585
1586 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1587 cron_setup_user();
1588 assign::cron();
1589
1590 // Regrade.
1591 $this->mark_submission($teacher, $assign, $student, 50.0);
1592
1593 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1594 cron_setup_user();
1595 $sink = $this->redirectMessages();
1596 assign::cron();
1597 $messages = $sink->get_messages();
1598
1599 $this->assertEquals(1, count($messages));
1600 $this->assertEquals(1, $messages[0]->notification);
1601 $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
47f48152
DW
1602 }
1603
70cfc878
JF
1604 /**
1605 * Test delivery of grade notifications as controlled by marking workflow.
1606 */
1607 public function test_markingworkflow_cron() {
757d5b7c
AN
1608 $this->resetAfterTest();
1609
70cfc878
JF
1610 // First run cron so there are no messages waiting to be sent (from other tests).
1611 cron_setup_user();
1612 assign::cron();
1613
757d5b7c
AN
1614 $course = $this->getDataGenerator()->create_course();
1615 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1616 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
70cfc878 1617
757d5b7c
AN
1618 // Now create an assignment and add some feedback.
1619 $this->setUser($teacher);
1620 $assign = $this->create_instance($course, [
1621 'sendstudentnotifications' => 1,
1622 'markingworkflow' => 1,
1623 ]);
1624
1625 // Mark a submission but set the workflowstate to an unreleased state.
1626 // This should not trigger a notification.
1627 $this->add_submission($student, $assign);
1628 $this->submit_for_grading($student, $assign);
1629 $this->mark_submission($teacher, $assign, $student, 50.0, [
1630 'sendstudentnotifications' => 1,
1631 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE,
1632 ]);
1633
1634 cron_setup_user();
1635 $sink = $this->redirectMessages();
1636 assign::cron();
1637 $messages = $sink->get_messages();
70cfc878 1638
757d5b7c 1639 $this->assertEquals(0, count($messages));
70cfc878 1640
757d5b7c
AN
1641 // Transition to the released state.
1642 $this->setUser($teacher);
1643 $submission = $assign->get_user_submission($student->id, true);
1644 $submission->workflowstate = ASSIGN_MARKING_WORKFLOW_STATE_RELEASED;
1645 $assign->testable_apply_grade_to_user($submission, $student->id, 0);
70cfc878
JF
1646
1647 // Now run cron and see that one message was sent.
70cfc878 1648 cron_setup_user();
757d5b7c 1649 $sink = $this->redirectMessages();
70cfc878
JF
1650 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
1651 assign::cron();
70cfc878 1652 $messages = $sink->get_messages();
757d5b7c 1653
70cfc878 1654 $this->assertEquals(1, count($messages));
757d5b7c 1655 $this->assertEquals(1, $messages[0]->notification);
70cfc878
JF
1656 $this->assertEquals($assign->get_instance()->name, $messages[0]->contexturlname);
1657 }
1658
cc350fd9 1659 public function test_cron_message_includes_courseid() {
757d5b7c
AN
1660 $this->resetAfterTest();
1661
cc350fd9
AD
1662 // First run cron so there are no messages waiting to be sent (from other tests).
1663 cron_setup_user();
1664 assign::cron();
1665
757d5b7c
AN
1666 $course = $this->getDataGenerator()->create_course();
1667 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1668 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
cc350fd9 1669
757d5b7c
AN
1670 // Now create an assignment and add some feedback.
1671 $this->setUser($teacher);
1672 $assign = $this->create_instance($course, [
1673 'sendstudentnotifications' => 1,
1674 ]);
cc350fd9 1675
757d5b7c
AN
1676 // Mark a submission but set the workflowstate to an unreleased state.
1677 // This should not trigger a notification.
1678 $this->add_submission($student, $assign);
1679 $this->submit_for_grading($student, $assign);
1680 $this->mark_submission($teacher, $assign, $student);
1681 phpunit_util::stop_message_redirection();
1682
1683 // Now run cron and see that one message was sent.
1684 cron_setup_user();
cc350fd9
AD
1685 $this->preventResetByRollback();
1686 $sink = $this->redirectEvents();
0e8b5160 1687 $this->expectOutputRegex('/Done processing 1 assignment submissions/');
cc350fd9
AD
1688 assign::cron();
1689
1690 $events = $sink->get_events();
cc350fd9 1691 $event = reset($events);
376a79c2 1692 $this->assertInstanceOf('\core\event\notification_sent', $event);
cc350fd9
AD
1693 $this->assertEquals($assign->get_course()->id, $event->other['courseid']);
1694 $sink->close();
1695 }
1696
47f48152 1697 public function test_is_graded() {
757d5b7c 1698 $this->resetAfterTest();
47f48152 1699
757d5b7c
AN
1700 $course = $this->getDataGenerator()->create_course();
1701 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1702 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1703 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
1704
1705 $assign = $this->create_instance($course);
47f48152 1706
757d5b7c
AN
1707 $this->add_submission($student, $assign);
1708 $this->submit_for_grading($student, $assign);
1709 $this->mark_submission($teacher, $assign, $student, 50.0);
1710
1711 $this->setUser($teacher);
1712 $this->assertEquals(true, $assign->testable_is_graded($student->id));
1713 $this->assertEquals(false, $assign->testable_is_graded($otherstudent->id));
47f48152
DW
1714 }
1715
4a47008c
DW
1716 public function test_can_grade() {
1717 global $DB;
1718
757d5b7c 1719 $this->resetAfterTest();
4a47008c 1720
757d5b7c
AN
1721 $course = $this->getDataGenerator()->create_course();
1722 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1723 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1724
1725 $assign = $this->create_instance($course);
1726
1727 $this->setUser($student);
4a47008c 1728 $this->assertEquals(false, $assign->can_grade());
757d5b7c
AN
1729
1730 $this->setUser($teacher);
4a47008c
DW
1731 $this->assertEquals(true, $assign->can_grade());
1732
faa5d173
SR
1733 // Test the viewgrades capability for other users.
1734 $this->setUser();
1735 $this->assertTrue($assign->can_grade($teacher->id));
1736 $this->assertFalse($assign->can_grade($student->id));
1737
4a47008c 1738 // Test the viewgrades capability - without mod/assign:grade.
757d5b7c
AN
1739 $this->setUser($student);
1740
4a47008c
DW
1741 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1742 assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
1743 $this->assertEquals(false, $assign->can_grade());
1744 }
1745
47f48152 1746 public function test_can_view_submission() {
4a47008c
DW
1747 global $DB;
1748
757d5b7c
AN
1749 $this->resetAfterTest();
1750
1751 $course = $this->getDataGenerator()->create_course();
1752 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1753 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
1754 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1755 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
1756 $suspendedstudent = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
1757
1758 $assign = $this->create_instance($course);
1759
1760 $this->setUser($student);
1761 $this->assertEquals(true, $assign->can_view_submission($student->id));
1762 $this->assertEquals(false, $assign->can_view_submission($otherstudent->id));
1763 $this->assertEquals(false, $assign->can_view_submission($teacher->id));
1764
1765 $this->setUser($teacher);
1766 $this->assertEquals(true, $assign->can_view_submission($student->id));
1767 $this->assertEquals(true, $assign->can_view_submission($otherstudent->id));
1768 $this->assertEquals(true, $assign->can_view_submission($teacher->id));
1769 $this->assertEquals(false, $assign->can_view_submission($suspendedstudent->id));
1770
1771 $this->setUser($editingteacher);
1772 $this->assertEquals(true, $assign->can_view_submission($student->id));
1773 $this->assertEquals(true, $assign->can_view_submission($otherstudent->id));
1774 $this->assertEquals(true, $assign->can_view_submission($teacher->id));
1775 $this->assertEquals(true, $assign->can_view_submission($suspendedstudent->id));
4a47008c
DW
1776
1777 // Test the viewgrades capability - without mod/assign:grade.
757d5b7c 1778 $this->setUser($student);
4a47008c
DW
1779 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1780 assign_capability('mod/assign:viewgrades', CAP_ALLOW, $studentrole->id, $assign->get_context()->id);
757d5b7c
AN
1781 $this->assertEquals(true, $assign->can_view_submission($student->id));
1782 $this->assertEquals(true, $assign->can_view_submission($otherstudent->id));
1783 $this->assertEquals(true, $assign->can_view_submission($teacher->id));
1784 $this->assertEquals(false, $assign->can_view_submission($suspendedstudent->id));
47f48152
DW
1785 }
1786
47f48152 1787 public function test_update_submission() {
757d5b7c 1788 $this->resetAfterTest();
47f48152 1789
757d5b7c
AN
1790 $course = $this->getDataGenerator()->create_course();
1791 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1792 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1793
1794 $assign = $this->create_instance($course);
1795
1796 $this->add_submission($student, $assign);
1797 $submission = $assign->get_user_submission($student->id, 0);
1798 $assign->testable_update_submission($submission, $student->id, true, true);
1799
1800 $this->setUser($teacher);
47f48152 1801
47f48152 1802 // Verify the gradebook update.
757d5b7c 1803 $gradinginfo = grade_get_grades($course->id, 'mod', 'assign', $assign->get_instance()->id, $student->id);
47f48152 1804
757d5b7c
AN
1805 $this->assertTrue(isset($gradinginfo->items[0]->grades[$student->id]));
1806 $this->assertEquals($student->id, $gradinginfo->items[0]->grades[$student->id]->usermodified);
1807 }
47f48152 1808
757d5b7c
AN
1809 public function test_update_submission_team() {
1810 $this->resetAfterTest();
47f48152 1811
757d5b7c
AN
1812 $course = $this->getDataGenerator()->create_course();
1813 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1814 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
e6cc5347 1815
757d5b7c
AN
1816 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1817 groups_add_member($group, $student);
1818
1819 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
1820 groups_add_member($group, $otherstudent);
1821
1822 $assign = $this->create_instance($course, [
1823 'teamsubmission' => 1,
1824 ]);
1825
1826 $gradinginfo = grade_get_grades($course->id, 'mod', 'assign', $assign->get_instance()->id, $student->id);
1827 $this->assertTrue(isset($gradinginfo->items[0]->grades[$student->id]));
1828 $this->assertNull($gradinginfo->items[0]->grades[$student->id]->usermodified);
1829
1830 $gradinginfo = grade_get_grades($course->id, 'mod', 'assign', $assign->get_instance()->id, $otherstudent->id);
1831 $this->asserttrue(isset($gradinginfo->items[0]->grades[$otherstudent->id]));
1832 $this->assertNull($gradinginfo->items[0]->grades[$otherstudent->id]->usermodified);
47f48152 1833
757d5b7c
AN
1834 $this->add_submission($student, $assign);
1835 $submission = $assign->get_group_submission($student->id, 0, true);
1836 $assign->testable_update_submission($submission, $student->id, true, true);
47f48152 1837
757d5b7c
AN
1838 // Verify the gradebook update for the student.
1839 $gradinginfo = grade_get_grades($course->id, 'mod', 'assign', $assign->get_instance()->id, $student->id);
1840
1841 $this->assertTrue(isset($gradinginfo->items[0]->grades[$student->id]));
1842 $this->assertEquals($student->id, $gradinginfo->items[0]->grades[$student->id]->usermodified);
1843
1844 // Verify the gradebook update for the other student.
1845 $gradinginfo = grade_get_grades($course->id, 'mod', 'assign', $assign->get_instance()->id, $otherstudent->id);
1846
1847 $this->assertTrue(isset($gradinginfo->items[0]->grades[$otherstudent->id]));
1848 $this->assertEquals($otherstudent->id, $gradinginfo->items[0]->grades[$otherstudent->id]->usermodified);
1849 }
1850
1851 public function test_update_submission_suspended() {
1852 $this->resetAfterTest();
1853
1854 $course = $this->getDataGenerator()->create_course();
1855 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1856 $student = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
1857
1858 $assign = $this->create_instance($course);
1859
1860 $this->add_submission($student, $assign);
1861 $submission = $assign->get_user_submission($student->id, 0);
1862 $assign->testable_update_submission($submission, $student->id, true, false);
1863
1864 $this->setUser($teacher);
1865
1866 // Verify the gradebook update.
1867 $gradinginfo = grade_get_grades($course->id, 'mod', 'assign', $assign->get_instance()->id, $student->id);
1868
1869 $this->assertTrue(isset($gradinginfo->items[0]->grades[$student->id]));
1870 $this->assertEquals($student->id, $gradinginfo->items[0]->grades[$student->id]->usermodified);
1871 }
1872
1873 public function test_update_submission_blind() {
1874 $this->resetAfterTest();
1875
1876 $course = $this->getDataGenerator()->create_course();
1877 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1878 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1879
1880 $assign = $this->create_instance($course, [
1881 'blindmarking' => 1,
1882 ]);
1883
1884 $this->add_submission($student, $assign);
1885 $submission = $assign->get_user_submission($student->id, 0);
1886 $assign->testable_update_submission($submission, $student->id, true, false);
1887
1888 // Verify the gradebook update.
1889 $gradinginfo = grade_get_grades($course->id, 'mod', 'assign', $assign->get_instance()->id, $student->id);
1890
1891 // The usermodified is not set because this is blind marked.
1892 $this->assertTrue(isset($gradinginfo->items[0]->grades[$student->id]));
1893 $this->assertNull($gradinginfo->items[0]->grades[$student->id]->usermodified);
47f48152
DW
1894 }
1895
79397b56 1896 public function test_group_submissions_submit_for_marking_requireallteammemberssubmit() {
2bd27878
DW
1897 global $PAGE;
1898
757d5b7c
AN
1899 $this->resetAfterTest();
1900
1901 $course = $this->getDataGenerator()->create_course();
1902 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1903 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1904
1905 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1906 groups_add_member($group, $student);
1907
1908 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
1909 groups_add_member($group, $otherstudent);
1910
1911 $assign = $this->create_instance($course, [
1912 'teamsubmission' => 1,
1913 'assignsubmission_onlinetext_enabled' => 1,
1914 'submissiondrafts' => 1,
1915 'requireallteammemberssubmit' => 1,
1916 ]);
1917
2bd27878 1918 // Now verify group assignments.
757d5b7c
AN
1919 $this->setUser($teacher);
1920 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
2bd27878 1921
2bd27878 1922 // Add a submission.
757d5b7c 1923 $this->add_submission($student, $assign);
2bd27878
DW
1924
1925 // Check we can see the submit button.
757d5b7c
AN
1926 $this->setUser($student);
1927 $output = $assign->view_student_summary($student, true);
2bd27878
DW
1928 $this->assertContains(get_string('submitassignment', 'assign'), $output);
1929
757d5b7c 1930 $submission = $assign->get_group_submission($student->id, 0, true);
2bd27878 1931 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
757d5b7c 1932 $assign->testable_update_submission($submission, $student->id, true, true);
2bd27878
DW
1933
1934 // Check that the student does not see "Submit" button.
757d5b7c 1935 $output = $assign->view_student_summary($student, true);
2bd27878
DW
1936 $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1937
1938 // Change to another user in the same group.
757d5b7c
AN
1939 $this->setUser($otherstudent);
1940 $output = $assign->view_student_summary($otherstudent, true);
2bd27878
DW
1941 $this->assertContains(get_string('submitassignment', 'assign'), $output);
1942
757d5b7c 1943 $submission = $assign->get_group_submission($otherstudent->id, 0, true);
2bd27878 1944 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
757d5b7c
AN
1945 $assign->testable_update_submission($submission, $otherstudent->id, true, true);
1946 $output = $assign->view_student_summary($otherstudent, true);
2bd27878
DW
1947 $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1948 }
1949
79397b56
DB
1950 public function test_group_submissions_submit_for_marking() {
1951 global $PAGE;
1952
757d5b7c
AN
1953 $this->resetAfterTest();
1954
1955 $course = $this->getDataGenerator()->create_course();
1956 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1957 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1958
1959 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1960 groups_add_member($group, $student);
1961
1962 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
1963 groups_add_member($group, $otherstudent);
1964
79397b56 1965 // Now verify group assignments.
757d5b7c 1966 $this->setUser($teacher);
a0f8db77 1967 $time = time();
757d5b7c
AN
1968 $assign = $this->create_instance($course, [
1969 'teamsubmission' => 1,
1970 'assignsubmission_onlinetext_enabled' => 1,
1971 'submissiondrafts' => 1,
1972 'requireallteammemberssubmit' => 0,
1973 'duedate' => $time - (2 * DAYSECS),
1974 ]);
1975 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
1976
79397b56 1977 // Add a submission.
757d5b7c 1978 $this->add_submission($student, $assign);
79397b56 1979
79397b56
DB
1980
1981 // Check we can see the submit button.
757d5b7c 1982 $output = $assign->view_student_summary($student, true);
79397b56
DB
1983 $this->assertContains(get_string('submitassignment', 'assign'), $output);
1984 $this->assertContains(get_string('timeremaining', 'assign'), $output);
a0f8db77 1985 $difftime = time() - $time;
757d5b7c 1986 $this->assertContains(get_string('overdue', 'assign', format_time((2 * DAYSECS) + $difftime)), $output);
79397b56 1987
757d5b7c 1988 $submission = $assign->get_group_submission($student->id, 0, true);
79397b56 1989 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
757d5b7c 1990 $assign->testable_update_submission($submission, $student->id, true, true);
79397b56
DB
1991
1992 // Check that the student does not see "Submit" button.
757d5b7c 1993 $output = $assign->view_student_summary($student, true);
79397b56
DB
1994 $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
1995
1996 // Change to another user in the same group.
757d5b7c
AN
1997 $this->setUser($otherstudent);
1998 $output = $assign->view_student_summary($otherstudent, true);
79397b56
DB
1999 $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
2000
2001 // Check that time remaining is not overdue.
2002 $this->assertContains(get_string('timeremaining', 'assign'), $output);
a0f8db77 2003 $difftime = time() - $time;
757d5b7c 2004 $this->assertContains(get_string('submittedlate', 'assign', format_time((2 * DAYSECS) + $difftime)), $output);
79397b56 2005
757d5b7c 2006 $submission = $assign->get_group_submission($otherstudent->id, 0, true);
79397b56 2007 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
757d5b7c
AN
2008 $assign->testable_update_submission($submission, $otherstudent->id, true, true);
2009 $output = $assign->view_student_summary($otherstudent, true);
79397b56
DB
2010 $this->assertNotContains(get_string('submitassignment', 'assign'), $output);
2011 }
2012
47f48152 2013 public function test_submissions_open() {
757d5b7c
AN
2014 global $DB;
2015
2016 $this->resetAfterTest();
2017
2018 $course = $this->getDataGenerator()->create_course();
2019 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2020 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2021 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2022 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2023 $suspendedstudent = $this->getDataGenerator()->create_and_enrol($course, 'student', null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
2024
2025 $this->setAdminUser();
47f48152
DW
2026
2027 $now = time();
757d5b7c
AN
2028 $tomorrow = $now + DAYSECS;
2029 $oneweek = $now + WEEKSECS;
2030 $yesterday = $now - DAYSECS;
47f48152 2031
757d5b7c
AN
2032 $assign = $this->create_instance($course);
2033 $this->assertEquals(true, $assign->testable_submissions_open($student->id));
47f48152 2034
757d5b7c
AN
2035 $assign = $this->create_instance($course, ['duedate' => $tomorrow]);
2036 $this->assertEquals(true, $assign->testable_submissions_open($student->id));
47f48152 2037
757d5b7c
AN
2038 $assign = $this->create_instance($course, ['duedate' => $yesterday]);
2039 $this->assertEquals(true, $assign->testable_submissions_open($student->id));
47f48152 2040
757d5b7c
AN
2041 $assign = $this->create_instance($course, ['duedate' => $yesterday, 'cutoffdate' => $tomorrow]);
2042 $this->assertEquals(true, $assign->testable_submissions_open($student->id));
47f48152 2043
757d5b7c
AN
2044 $assign = $this->create_instance($course, ['duedate' => $yesterday, 'cutoffdate' => $yesterday]);
2045 $this->assertEquals(false, $assign->testable_submissions_open($student->id));
47f48152 2046
757d5b7c
AN
2047 $assign->testable_save_user_extension($student->id, $tomorrow);
2048 $this->assertEquals(true, $assign->testable_submissions_open($student->id));
47f48152 2049
757d5b7c
AN
2050 $assign = $this->create_instance($course, ['submissiondrafts' => 1]);
2051 $this->assertEquals(true, $assign->testable_submissions_open($student->id));
47f48152 2052
757d5b7c
AN
2053 $this->setUser($student);
2054 $submission = $assign->get_user_submission($student->id, true);
47f48152 2055 $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
757d5b7c
AN
2056 $assign->testable_update_submission($submission, $student->id, true, false);
2057
2058 $this->setUser($teacher);
2059 $this->assertEquals(false, $assign->testable_submissions_open($student->id));
47f48152
DW
2060 }
2061
2062 public function test_get_graders() {
757d5b7c
AN
2063 global $DB;
2064
2065 $this->resetAfterTest();
2066
2067 $course = $this->getDataGenerator()->create_course();
2068 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2069 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2070 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2071
2072 $this->setAdminUser();
47f48152 2073
f268fb5d 2074 // Create an assignment with no groups.
757d5b7c
AN
2075 $assign = $this->create_instance($course);
2076 $this->assertCount(2, $assign->testable_get_graders($student->id));
2077 }
2078
2079 public function test_get_graders_separate_groups() {
2080 global $DB;
2081
2082 $this->resetAfterTest();
2083
2084 $course = $this->getDataGenerator()->create_course();
2085 $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2086 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2087 $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2088 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2089 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2090 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2091
2092 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
2093 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
2094 groups_add_member($group1, $student);
2095
2096 $this->setAdminUser();
47f48152 2097
47f48152 2098 // Force create an assignment with SEPARATEGROUPS.
757d5b7c
AN
2099 $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
2100 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
2101
2102 $assign = $this->create_instance($course, [
2103 'groupingid' => $grouping->id,
2104 'groupmode' => SEPARATEGROUPS,
2105 ]);
2106
2107 $this->assertCount(4, $assign->testable_get_graders($student->id));
2108
f268fb5d
DW
2109 // Note the second student is in a group that is not in the grouping.
2110 // This means that we get all graders that are not in a group in the grouping.
757d5b7c 2111 $this->assertCount(4, $assign->testable_get_graders($otherstudent->id));
47f48152
DW
2112 }
2113
83360c8d
S
2114 public function test_get_notified_users() {
2115 global $CFG, $DB;
2116
757d5b7c
AN
2117 $this->resetAfterTest();
2118
2119 $course = $this->getDataGenerator()->create_course();
2120 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
2121 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
2122 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id, 'groupingid' => $grouping->id));
2123
2124 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2125 groups_add_member($group1, $teacher);
2126
2127 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2128 groups_add_member($group1, $editingteacher);
2129
2130 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2131 groups_add_member($group1, $student);
2132
2133 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2134 $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2135
83360c8d 2136 $capability = 'mod/assign:receivegradernotifications';
757d5b7c 2137 $coursecontext = context_course::instance($course->id);
83360c8d
S
2138 $role = $DB->get_record('role', array('shortname' => 'teacher'));
2139
757d5b7c 2140 $this->setUser($teacher);
83360c8d
S
2141
2142 // Create an assignment with no groups.
757d5b7c 2143 $assign = $this->create_instance($course);
83360c8d 2144
757d5b7c 2145 $this->assertCount(3, $assign->testable_get_notifiable_users($student->id));
83360c8d
S
2146
2147 // Change nonediting teachers role to not receive grader notifications.
2148 assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
2149
757d5b7c
AN
2150 // Only the editing teachers will be returned.
2151 $this->assertCount(1, $assign->testable_get_notifiable_users($student->id));
2152
2153 // Note the second student is in a group that is not in the grouping.
2154 // This means that we get all graders that are not in a group in the grouping.
2155 $this->assertCount(1, $assign->testable_get_notifiable_users($otherstudent->id));
2156 }
2157
2158 public function test_get_notified_users_in_grouping() {
2159 global $CFG, $DB;
2160
2161 $this->resetAfterTest();
2162
2163 $course = $this->getDataGenerator()->create_course();
2164 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
2165 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
2166 $this->getDataGenerator()->create_grouping_group(array('groupid' => $group1->id, 'groupingid' => $grouping->id));
2167
2168 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2169 groups_add_member($group1, $teacher);
2170
2171 $editingteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2172 groups_add_member($group1, $editingteacher);
2173
2174 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2175 groups_add_member($group1, $student);
2176
2177 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2178 $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2179
2180 // Force create an assignment with SEPARATEGROUPS.
2181 $assign = $this->create_instance($course, [
2182 'groupingid' => $grouping->id,
2183 'groupmode' => SEPARATEGROUPS,
2184 ]);
83360c8d 2185
757d5b7c
AN
2186 // Student is in a group - only the tacher and editing teacher in the group shoudl be present.
2187 $this->setUser($student);
2188 $this->assertCount(2, $assign->testable_get_notifiable_users($student->id));
83360c8d 2189
83360c8d
S
2190 // Note the second student is in a group that is not in the grouping.
2191 // This means that we get all graders that are not in a group in the grouping.
757d5b7c 2192 $this->assertCount(1, $assign->testable_get_notifiable_users($otherstudent->id));
83360c8d
S
2193
2194 // Change nonediting teachers role to not receive grader notifications.
757d5b7c
AN
2195 $capability = 'mod/assign:receivegradernotifications';
2196 $coursecontext = context_course::instance($course->id);
2197 $role = $DB->get_record('role', ['shortname' => 'teacher']);
83360c8d
S
2198 assign_capability($capability, CAP_PROHIBIT, $role->id, $coursecontext);
2199
757d5b7c
AN
2200 // Only the editing teachers will be returned.
2201 $this->assertCount(1, $assign->testable_get_notifiable_users($student->id));
2202
83360c8d
S
2203 // Note the second student is in a group that is not in the grouping.
2204 // This means that we get all graders that are not in a group in the grouping.
757d5b7c
AN
2205 // Unfortunately there are no editing teachers who are not in a group.
2206 $this->assertCount(0, $assign->testable_get_notifiable_users($otherstudent->id));
83360c8d
S
2207 }
2208
20b7dcfc
DW
2209 public function test_group_members_only() {
2210 global $CFG;
2211
757d5b7c 2212 $this->resetAfterTest();
20b7dcfc 2213
757d5b7c
AN
2214 $course = $this->getDataGenerator()->create_course();
2215 $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id));
2216 $group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
2217 $this->getDataGenerator()->create_grouping_group([
2218 'groupid' => $group1->id,
2219 'groupingid' => $grouping->id,
2220 ]);
20b7dcfc 2221
757d5b7c
AN
2222 $group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
2223 $this->getDataGenerator()->create_grouping_group([
2224 'groupid' => $group2->id,
2225 'groupingid' => $grouping->id,
2226 ]);
2227
2228 $group3 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
2229
2230 // Add users in the following groups
2231 // - Teacher - Group 1.
2232 // - Student - Group 1.
2233 // - Student - Group 2.
2234 // - Student - Unrelated Group
2235 // - Student - No group.
2236 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2237 groups_add_member($group1, $teacher);
2238
2239 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2240 groups_add_member($group1, $student);
2241
2242 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2243 groups_add_member($group2, $otherstudent);
2244
2245 $yetotherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
2246 groups_add_member($group2, $otherstudent);
20b7dcfc 2247
757d5b7c 2248 $this->getDataGenerator()->create_and_enrol($course, 'student');
20b7dcfc 2249
757d5b7c
AN
2250 $this->setAdminUser();
2251
2252 $CFG->enableavailability = true;
2253 $assign = $this->create_instance($course, [], [
2254 'availability' => json_encode(
2255 \core_availability\tree::get_root_json([\availability_grouping\condition::get_json()])
2256 ),
2257 'groupingid' => $grouping->id,
2258 ]);
2259
2260 // The two students in groups should be returned, but not the teacher in the group, or the student not in the
2261 // group, or the student in an unrelated group.
2262 $this->setUser($teacher);
2263 $participants = $assign->list_participants(0, true);
2264 $this->assertCount(2, $participants);
2265 $this->assertTrue(isset($participants[$student->id]));
2266 $this->assertTrue(isset($participants[$otherstudent->id]));
20b7dcfc
DW
2267 }
2268
47f48152 2269 public function test_get_uniqueid_for_user() {
757d5b7c
AN
2270 $this->resetAfterTest();
2271
2272 $course = $this->getDataGenerator()->create_course();
2273 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2274 $students = [];
2275 for ($i = 0; $i < 10; $i++) {
2276 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2277 $students[$student->id] = $student;
2278 }
2279
2280 $this->setUser($teacher);
2281 $assign = $this->create_instance($course);
47f48152 2282
757d5b7c 2283 foreach ($students as $student) {
47f48152
DW
2284 $uniqueid = $assign->get_uniqueid_for_user($student->id);
2285 $this->assertEquals($student->id, $assign->get_user_id_for_uniqueid($uniqueid));
2286 }
2287 }
2288
46692c3a 2289 public function test_show_student_summary() {
c2114099 2290 global $CFG, $PAGE;
46692c3a 2291
757d5b7c
AN
2292 $this->resetAfterTest();
2293
2294 $course = $this->getDataGenerator()->create_course();
2295 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2296 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2297 $this->setUser($teacher);
2298 $assign = $this->create_instance($course);
2299 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
46692c3a
DW
2300
2301 // No feedback should be available because this student has not been graded.
757d5b7c
AN
2302 $this->setUser($student);
2303 $output = $assign->view_student_summary($student, true);
2304 $this->assertNotRegexp('/Feedback/', $output, 'Do not show feedback if there is no grade');
2305
46692c3a 2306 // Simulate adding a grade.
757d5b7c
AN
2307 $this->add_submission($student, $assign);
2308 $this->submit_for_grading($student, $assign);
2309 $this->mark_submission($teacher, $assign, $student);
46692c3a 2310
b0da618b 2311 // Now we should see the feedback.
757d5b7c
AN
2312 $this->setUser($student);
2313 $output = $assign->view_student_summary($student, true);
2314 $this->assertRegexp('/Feedback/', $output, 'Show feedback if there is a grade');
46692c3a
DW
2315
2316 // Now hide the grade in gradebook.
757d5b7c 2317 $this->setUser($teacher);
46692c3a
DW
2318 require_once($CFG->libdir.'/gradelib.php');
2319 $gradeitem = new grade_item(array(
2320 'itemtype' => 'mod',
2321 'itemmodule' => 'assign',
2322 'iteminstance' => $assign->get_instance()->id,
757d5b7c 2323 'courseid' => $course->id));
46692c3a
DW
2324
2325 $gradeitem->set_hidden(1, false);
2326
2327 // No feedback should be available because the grade is hidden.
757d5b7c
AN
2328 $this->setUser($student);
2329 $output = $assign->view_student_summary($student, true);
2330 $this->assertNotRegexp('/Feedback/', $output, 'Do not show feedback if the grade is hidden in the gradebook');
2331 }
46692c3a 2332
757d5b7c
AN
2333 public function test_show_student_summary_with_feedback() {
2334 global $CFG, $PAGE;
46692c3a 2335
757d5b7c
AN
2336 $this->resetAfterTest();
2337
2338 $course = $this->getDataGenerator()->create_course();
2339 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2340 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2341 $this->setUser($teacher);
2342 $assign = $this->create_instance($course, [
2343 'assignfeedback_comments_enabled' => 1
2344 ]);
2345 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
2346
2347 // No feedback should be available because this student has not been graded.
2348 $this->setUser($student);
2349 $output = $assign->view_student_summary($student, true);
2350 $this->assertNotRegexp('/Feedback/', $output);
2351
2352 // Simulate adding a grade.
2353 $this->add_submission($student, $assign);
2354 $this->submit_for_grading($student, $assign);
2355 $this->mark_submission($teacher, $assign, $student, null, [
2356 'assignfeedbackcomments_editor' => [
2357 'text' => 'Tomato sauce',
2358 'format' => FORMAT_MOODLE,
2359 ],
2360 ]);
2361
2362 // Should have feedback but no grade.
2363 $this->setUser($student);
2364 $output = $assign->view_student_summary($student, true);
2365 $this->assertRegexp('/Feedback/', $output);
2366 $this->assertRegexp('/Tomato sauce/', $output);
2367 $this->assertNotRegexp('/Grade/', $output, 'Do not show grade when there is no grade.');
2368 $this->assertNotRegexp('/Graded on/', $output, 'Do not show graded date when there is no grade.');
2369
2370 // Add a grade now.
2371 $this->mark_submission($teacher, $assign, $student, 50.0, [
2372 'assignfeedbackcomments_editor' => [
2373 'text' => 'Bechamel sauce',
2374 'format' => FORMAT_MOODLE,
2375 ],
2376 ]);
46692c3a 2377
b0da618b 2378 // Should have feedback but no grade.
757d5b7c
AN
2379 $this->setUser($student);
2380 $output = $assign->view_student_summary($student, true);
2381 $this->assertNotRegexp('/Tomato sauce/', $output);
2382 $this->assertRegexp('/Bechamel sauce/', $output);
2383 $this->assertRegexp('/Grade/', $output);
2384 $this->assertRegexp('/Graded on/', $output);
45aac51a 2385
ea698284 2386 // Now hide the grade in gradebook.
757d5b7c 2387 $this->setUser($teacher);
ea698284
AD
2388 $gradeitem = new grade_item(array(
2389 'itemtype' => 'mod',
2390 'itemmodule' => 'assign',
2391 'iteminstance' => $assign->get_instance()->id,
757d5b7c 2392 'courseid' => $course->id));
ea698284
AD
2393
2394 $gradeitem->set_hidden(1, false);
2395
2396 // No feedback should be available because the grade is hidden.
757d5b7c
AN
2397 $this->setUser($student);
2398 $output = $assign->view_student_summary($student, true);
2399 $this->assertNotRegexp('/Feedback/', $output, 'Do not show feedback if the grade is hidden in the gradebook');
46692c3a
DW
2400 }
2401
757d5b7c
AN
2402 /**
2403 * Test reopen behavior when in "Manual" mode.
2404 */
df211804
DW
2405 public function test_attempt_reopen_method_manual() {
2406 global $PAGE;
2407
757d5b7c
AN
2408 $this->resetAfterTest();
2409 $course = $this->getDataGenerator()->create_course();
2410 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2411 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2412
2413 $assign = $this->create_instance($course, [
2414 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
2415 'maxattempts' => 3,
2416 'submissiondrafts' => 1,
2417 'assignsubmission_onlinetext_enabled' => 1,
2418 ]);
2419 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
df211804
DW
2420
2421 // Student should be able to see an add submission button.
757d5b7c
AN
2422 $this->setUser($student);
2423 $output = $assign->view_student_summary($student, true);
df211804
DW
2424 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2425
2426 // Add a submission.
757d5b7c
AN
2427 $this->add_submission($student, $assign);
2428 $this->submit_for_grading($student, $assign);
df211804
DW
2429
2430 // Verify the student cannot make changes to the submission.
757d5b7c 2431 $output = $assign->view_student_summary($student, true);
df211804
DW
2432 $this->assertEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2433
2434 // Mark the submission.
757d5b7c 2435 $this->mark_submission($teacher, $assign, $student);
df211804
DW
2436
2437 // Check the student can see the grade.
757d5b7c
AN
2438 $this->setUser($student);
2439 $output = $assign->view_student_summary($student, true);
df211804
DW
2440 $this->assertNotEquals(false, strpos($output, '50.0'));
2441
2442 // Allow the student another attempt.
757d5b7c
AN
2443 $teacher->ignoresesskey = true;
2444 $this->setUser($teacher);
2445 $result = $assign->testable_process_add_attempt($student->id);
df211804
DW
2446 $this->assertEquals(true, $result);
2447
2448 // Check that the previous attempt is now in the submission history table.
757d5b7c
AN
2449 $this->setUser($student);
2450 $output = $assign->view_student_summary($student, true);
df211804
DW
2451 // Need a better check.
2452 $this->assertNotEquals(false, strpos($output, 'Submission text'), 'Contains: Submission text');
2453
2454 // Check that the student now has a button for Add a new attempt".
2455 $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2456 // Check that the student now does not have a button for Submit.
2457 $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
2458
2459 // Check that the student now has a submission history.
2460 $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
2461
757d5b7c 2462 $this->setUser($teacher);
df211804
DW
2463 // Check that the grading table loads correctly and contains this user.
2464 // This is also testing that we do not get duplicate rows in the grading table.
2465 $gradingtable = new assign_grading_table($assign, 100, '', 0, true);
2466 $output = $assign->get_renderer()->render($gradingtable);
757d5b7c 2467 $this->assertEquals(true, strpos($output, $student->lastname));
df211804
DW
2468
2469 // Should be 1 not 2.
2470 $this->assertEquals(1, $assign->count_submissions());
2471 $this->assertEquals(1, $assign->count_submissions_with_status('reopened'));
2472 $this->assertEquals(0, $assign->count_submissions_need_grading());
2473 $this->assertEquals(1, $assign->count_grades());
2474
2475 // Change max attempts to unlimited.
2476 $formdata = clone($assign->get_instance());
2477 $formdata->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS;
2478 $formdata->instance = $formdata->id;
2479 $assign->update_instance($formdata);
2480
9e3eee67 2481 // Mark the submission again.
757d5b7c 2482 $this->mark_submission($teacher, $assign, $student, 60.0, [], 1);
9e3eee67
DW
2483
2484 // Check the grade exists.
757d5b7c
AN
2485 $this->setUser($teacher);
2486 $grades = $assign->get_user_grades_for_gradebook($student->id);
2487 $this->assertEquals(60, (int) $grades[$student->id]->rawgrade);
9e3eee67
DW
2488
2489 // Check we can reopen still.
757d5b7c 2490 $result = $assign->testable_process_add_attempt($student->id);
df211804
DW
2491 $this->assertEquals(true, $result);
2492
9e3eee67 2493 // Should no longer have a grade because there is no grade for the latest attempt.
757d5b7c 2494 $grades = $assign->get_user_grades_for_gradebook($student->id);
9e3eee67 2495 $this->assertEmpty($grades);
df211804 2496 }
46692c3a 2497
e77bacab
MP
2498 /**
2499 * Test reopen behavior when in "Reopen until pass" mode.
2500 */
2501 public function test_attempt_reopen_method_untilpass() {
2502 global $PAGE;
2503
757d5b7c
AN
2504 $this->resetAfterTest();
2505 $course = $this->getDataGenerator()->create_course();
2506 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2507 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2508
2509 $assign = $this->create_instance($course, [
2510 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS,
e77bacab
MP
2511 'maxattempts' => 3,
2512 'submissiondrafts' => 1,
757d5b7c
AN
2513 'assignsubmission_onlinetext_enabled' => 1,
2514 ]);
2515 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
e77bacab
MP
2516
2517 // Set grade to pass to 80.
2518 $gradeitem = $assign->get_grade_item();
2519 $gradeitem->gradepass = '80.0';
2520 $gradeitem->update();
2521
2522 // Student should be able to see an add submission button.
757d5b7c
AN
2523 $this->setUser($student);
2524 $output = $assign->view_student_summary($student, true);
e77bacab
MP
2525 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2526
2527 // Add a submission.
757d5b7c
AN
2528 $this->add_submission($student, $assign);
2529 $this->submit_for_grading($student, $assign);
e77bacab
MP
2530
2531 // Verify the student cannot make a new attempt.
757d5b7c 2532 $output = $assign->view_student_summary($student, true);
e77bacab
MP
2533 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2534
2535 // Mark the submission as non-passing.
757d5b7c 2536 $this->mark_submission($teacher, $assign, $student, 50.0);
e77bacab
MP
2537
2538 // Check the student can see the grade.
757d5b7c
AN
2539 $this->setUser($student);
2540 $output = $assign->view_student_summary($student, true);
e77bacab
MP
2541 $this->assertNotEquals(false, strpos($output, '50.0'));
2542
2543 // Check that the student now has a button for Add a new attempt.
757d5b7c 2544 $output = $assign->view_student_summary($student, true);
e77bacab
MP
2545 $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2546
2547 // Check that the student now does not have a button for Submit.
2548 $this->assertEquals(false, strpos($output, get_string('submitassignment', 'assign')));
2549
2550 // Check that the student now has a submission history.
2551 $this->assertNotEquals(false, strpos($output, get_string('attempthistory', 'assign')));
2552
2553 // Add a second submission.
757d5b7c
AN
2554 $this->add_submission($student, $assign);
2555 $this->submit_for_grading($student, $assign);
e77bacab
MP
2556
2557 // Mark the submission as passing.
757d5b7c 2558 $this->mark_submission($teacher, $assign, $student, 80.0);
e77bacab
MP
2559
2560 // Check that the student does not have a button for Add a new attempt.
757d5b7c
AN
2561 $this->setUser($student);
2562 $output = $assign->view_student_summary($student, true);
e77bacab
MP
2563 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2564
2565 // Re-mark the submission as not passing.
757d5b7c 2566 $this->mark_submission($teacher, $assign, $student, 40.0, [], 1);
e77bacab
MP
2567
2568 // Check that the student now has a button for Add a new attempt.
757d5b7c
AN
2569 $this->setUser($student);
2570 $output = $assign->view_student_summary($student, true);
2571 $this->assertRegexp('/' . get_string('addnewattempt', 'assign') . '/', $output);
e77bacab 2572 $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
757d5b7c 2573 }
e77bacab 2574
757d5b7c
AN
2575 public function test_attempt_reopen_method_untilpass_passing() {
2576 global $PAGE;
e77bacab 2577
757d5b7c
AN
2578 $this->resetAfterTest();
2579 $course = $this->getDataGenerator()->create_course();
2580 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2581 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2582
2583 $assign = $this->create_instance($course, [
2584 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS,
2585 'maxattempts' => 3,
2586 'submissiondrafts' => 1,
2587 'assignsubmission_onlinetext_enabled' => 1,
2588 ]);
2589 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
2590
2591 // Set grade to pass to 80.
2592 $gradeitem = $assign->get_grade_item();
2593 $gradeitem->gradepass = '80.0';
2594 $gradeitem->update();
2595
2596 // Student should be able to see an add submission button.
2597 $this->setUser($student);
2598 $output = $assign->view_student_summary($student, true);
2599 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2600
2601 // Add a submission as a student.
2602 $this->add_submission($student, $assign);
2603 $this->submit_for_grading($student, $assign);
e77bacab
MP
2604
2605 // Mark the submission as passing.
757d5b7c 2606 $this->mark_submission($teacher, $assign, $student, 100.0);
e77bacab
MP
2607
2608 // Check the student can see the grade.
757d5b7c
AN
2609 $this->setUser($student);
2610 $output = $assign->view_student_summary($student, true);
e77bacab
MP
2611 $this->assertNotEquals(false, strpos($output, '100.0'));
2612
2613 // Check that the student does not have a button for Add a new attempt.
757d5b7c 2614 $output = $assign->view_student_summary($student, true);
e77bacab 2615 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
757d5b7c
AN
2616 }
2617
2618 public function test_attempt_reopen_method_untilpass_no_passing_requirement() {
2619 global $PAGE;
2620
2621 $this->resetAfterTest();
2622 $course = $this->getDataGenerator()->create_course();
2623 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2624 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2625
2626 $assign = $this->create_instance($course, [
2627 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS,
2628 'maxattempts' => 3,
2629 'submissiondrafts' => 1,
2630 'assignsubmission_onlinetext_enabled' => 1,
2631 ]);
2632 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
e77bacab
MP
2633
2634 // Set grade to pass to 0, so that no attempts should reopen.
2635 $gradeitem = $assign->get_grade_item();
2636 $gradeitem->gradepass = '0';
2637 $gradeitem->update();
2638
757d5b7c
AN
2639 // Student should be able to see an add submission button.
2640 $this->setUser($student);
2641 $output = $assign->view_student_summary($student, true);
2642 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
e77bacab 2643
757d5b7c
AN
2644 // Add a submission.
2645 $this->add_submission($student, $assign);
2646 $this->submit_for_grading($student, $assign);
e77bacab 2647
757d5b7c
AN
2648 // Mark the submission with any grade.
2649 $this->mark_submission($teacher, $assign, $student, 0.0);
e77bacab
MP
2650
2651 // Check the student can see the grade.
757d5b7c
AN
2652 $this->setUser($student);
2653 $output = $assign->view_student_summary($student, true);
e77bacab
MP
2654 $this->assertNotEquals(false, strpos($output, '0.0'));
2655
2656 // Check that the student does not have a button for Add a new attempt.
757d5b7c 2657 $output = $assign->view_student_summary($student, true);
e77bacab
MP
2658 $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign')));
2659 }
2660
757d5b7c
AN
2661 /**
2662 * Test student visibility for each stage of the marking workflow.
2663 */
f8d107b3
DM
2664 public function test_markingworkflow() {
2665 global $PAGE;
2666
757d5b7c
AN
2667 $this->resetAfterTest();
2668 $course = $this->getDataGenerator()->create_course();
2669 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2670 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2671
2672 $assign = $this->create_instance($course, [
2673 'markingworkflow' => 1,
2674 ]);
2675
2676 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
f8d107b3 2677
13e82f1c 2678 // Mark the submission and set to notmarked.
757d5b7c
AN
2679 $this->mark_submission($teacher, $assign, $student, 50.0, [
2680 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_NOTMARKED,
2681 ]);
f8d107b3
DM
2682
2683 // Check the student can't see the grade.
757d5b7c
AN
2684 $this->setUser($student);
2685 $output = $assign->view_student_summary($student, true);
f8d107b3
DM
2686 $this->assertEquals(false, strpos($output, '50.0'));
2687
5047b0ea 2688 // Make sure the grade isn't pushed to the gradebook.
757d5b7c 2689 $grades = $assign->get_user_grades_for_gradebook($student->id);
5047b0ea
TB
2690 $this->assertEmpty($grades);
2691
13e82f1c 2692 // Mark the submission and set to inmarking.
757d5b7c
AN
2693 $this->mark_submission($teacher, $assign, $student, 50.0, [
2694 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_INMARKING,
2695 ]);
f8d107b3
DM
2696
2697 // Check the student can't see the grade.
757d5b7c
AN
2698 $this->setUser($student);
2699 $output = $assign->view_student_summary($student, true);
f8d107b3
DM
2700 $this->assertEquals(false, strpos($output, '50.0'));
2701
5047b0ea 2702 // Make sure the grade isn't pushed to the gradebook.
757d5b7c 2703 $grades = $assign->get_user_grades_for_gradebook($student->id);
5047b0ea
TB
2704 $this->assertEmpty($grades);
2705
13e82f1c 2706 // Mark the submission and set to readyforreview.
757d5b7c
AN
2707 $this->mark_submission($teacher, $assign, $student, 50.0, [
2708 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_READYFORREVIEW,
2709 ]);
f8d107b3
DM
2710
2711 // Check the student can't see the grade.
757d5b7c
AN
2712 $this->setUser($student);
2713 $output = $assign->view_student_summary($student, true);
f8d107b3
DM
2714 $this->assertEquals(false, strpos($output, '50.0'));
2715
5047b0ea 2716 // Make sure the grade isn't pushed to the gradebook.
757d5b7c 2717 $grades = $assign->get_user_grades_for_gradebook($student->id);
5047b0ea
TB
2718 $this->assertEmpty($grades);
2719
13e82f1c 2720 // Mark the submission and set to inreview.
757d5b7c
AN
2721 $this->mark_submission($teacher, $assign, $student, 50.0, [
2722 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_INREVIEW,
2723 ]);
f8d107b3
DM
2724
2725 // Check the student can't see the grade.
757d5b7c
AN
2726 $this->setUser($student);
2727 $output = $assign->view_student_summary($student, true);
f8d107b3
DM
2728 $this->assertEquals(false, strpos($output, '50.0'));
2729
5047b0ea 2730 // Make sure the grade isn't pushed to the gradebook.
757d5b7c 2731 $grades = $assign->get_user_grades_for_gradebook($student->id);
5047b0ea
TB
2732 $this->assertEmpty($grades);
2733
13e82f1c 2734 // Mark the submission and set to readyforrelease.
757d5b7c
AN
2735 $this->mark_submission($teacher, $assign, $student, 50.0, [
2736 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_READYFORRELEASE,
2737 ]);
f8d107b3
DM
2738
2739 // Check the student can't see the grade.
757d5b7c
AN
2740 $this->setUser($student);
2741 $output = $assign->view_student_summary($student, true);
f8d107b3
DM
2742 $this->assertEquals(false, strpos($output, '50.0'));
2743
5047b0ea 2744 // Make sure the grade isn't pushed to the gradebook.
757d5b7c 2745 $grades = $assign->get_user_grades_for_gradebook($student->id);
5047b0ea
TB
2746 $this->assertEmpty($grades);
2747
13e82f1c 2748 // Mark the submission and set to released.
757d5b7c
AN
2749 $this->mark_submission($teacher, $assign, $student, 50.0, [
2750 'workflowstate' => ASSIGN_MARKING_WORKFLOW_STATE_RELEASED,
2751 ]);
f8d107b3
DM
2752
2753 // Check the student can see the grade.
757d5b7c
AN
2754 $this->setUser($student);
2755 $output = $assign->view_student_summary($student, true);
f8d107b3 2756 $this->assertNotEquals(false, strpos($output, '50.0'));
5047b0ea
TB
2757
2758 // Make sure the grade is pushed to the gradebook.
757d5b7c
AN
2759 $grades = $assign->get_user_grades_for_gradebook($student->id);
2760 $this->assertEquals(50, (int)$grades[$student->id]->rawgrade);
f8d107b3
DM
2761 }
2762
757d5b7c
AN
2763 /**
2764 * Test that a student allocated a specific marker is only shown to that marker.
2765 */
f8d107b3
DM
2766 public function test_markerallocation() {
2767 global $PAGE;
2768
757d5b7c
AN
2769 $this->resetAfterTest();
2770 $course = $this->getDataGenerator()->create_course();
2771 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2772 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2773 $otherteacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
2774
2775 $assign = $this->create_instance($course, [
2776 'markingworkflow' => 1,
2777 'markingallocation' => 1
2778 ]);
2779
2780 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
f8d107b3 2781
13e82f1c 2782 // Allocate marker to submission.
757d5b7c
AN
2783 $this->mark_submission($teacher, $assign, $student, null, [
2784 'allocatedmarker' => $teacher->id,
2785 ]);
f8d107b3 2786
13e82f1c 2787 // Check the allocated marker can view the submission.
757d5b7c 2788 $this->setUser($teacher);
230ae095 2789 $users = $assign->list_participants(0, true);
757d5b7c
AN
2790 $this->assertEquals(1, count($users));
2791 $this->assertTrue(isset($users[$student->id]));
230ae095
DW
2792
2793 $cm = get_coursemodule_from_instance('assign', $assign->get_instance()->id);
2794 $context = context_module::instance($cm->id);
c9577071 2795 $assign = new mod_assign_testable_assign($context, $cm, $course);
757d5b7c 2796
13e82f1c 2797 // Check that other teachers can't view this submission.
757d5b7c 2798 $this->setUser($otherteacher);
230ae095
DW
2799 $users = $assign->list_participants(0, true);
2800 $this->assertEquals(0, count($users));
f8d107b3 2801 }
76e77b05 2802
52f3e060 2803 /**
757d5b7c 2804 * Ensure that a teacher cannot submit for students as standard.
52f3e060 2805 */
57fbd5f9
DW
2806 public function test_teacher_submit_for_student() {
2807 global $PAGE;
2808
757d5b7c
AN
2809 $this->resetAfterTest();
2810 $course = $this->getDataGenerator()->create_course();
2811 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2812 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
57fbd5f9 2813
757d5b7c
AN
2814 $assign = $this->create_instance($course, [
2815 'assignsubmission_onlinetext_enabled' => 1,
2816 'submissiondrafts' => 1,
2817 ]);
57fbd5f9 2818
757d5b7c 2819 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
57fbd5f9 2820
757d5b7c
AN
2821 // Add a submission but do not submit.
2822 $this->add_submission($student, $assign, 'Student submission text');
57fbd5f9 2823
757d5b7c
AN
2824 $this->setUser($student);
2825 $output = $assign->view_student_summary($student, true);
57fbd5f9
DW
2826 $this->assertContains('Student submission text', $output, 'Contains student submission text');
2827
757d5b7c
AN
2828 // Check that a teacher can not edit the submission as they do not have the capability.
2829 $this->setUser($teacher);
2830 $this->expectException('moodle_exception');
2831 $this->expectExceptionMessage('error/nopermission');
2832 $this->add_submission($student, $assign, 'Teacher edited submission text', false);
2833 }
2834
2835 /**
2836 * Ensure that a teacher with the editothersubmission capability can submit on behalf of a student.
2837 */
2838 public function test_teacher_submit_for_student_with_capability() {
2839 global $PAGE;
2840
2841 $this->resetAfterTest();
2842 $course = $this->getDataGenerator()->create_course();
2843 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2844 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2845 $otherteacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
2846
2847 $assign = $this->create_instance($course, [
2848 'assignsubmission_onlinetext_enabled' => 1,
2849 'submissiondrafts' => 1,
2850 ]);
57fbd5f9
DW
2851
2852 // Add the required capability.
2853 $roleid = create_role('Dummy role', 'dummyrole', 'dummy role description');
2854 assign_capability('mod/assign:editothersubmission', CAP_ALLOW, $roleid, $assign->get_context()->id);
757d5b7c 2855 role_assign($roleid, $teacher->id, $assign->get_context()->id);
57fbd5f9
DW
2856 accesslib_clear_all_caches_for_unit_testing();
2857
757d5b7c
AN
2858 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
2859
2860 // Add a submission but do not submit.
2861 $this->add_submission($student, $assign, 'Student submission text');
2862
2863 $this->setUser($student);
2864 $output = $assign->view_student_summary($student, true);
2865 $this->assertContains('Student submission text', $output, 'Contains student submission text');
2866
2867 // Check that a teacher can edit the submission.
2868 $this->setUser($teacher);
2869 $this->add_submission($student, $assign, 'Teacher edited submission text', false);
2870
2871 $this->setUser($student);
2872 $output = $assign->view_student_summary($student, true);
2873 $this->assertNotContains('Student submission text', $output, 'Contains student submission text');
2874 $this->assertContains('Teacher edited submission text', $output, 'Contains teacher edited submission text');
57fbd5f9
DW
2875
2876 // Check that the teacher can submit the students work.
757d5b7c
AN
2877 $this->setUser($teacher);
2878 $this->submit_for_grading($student, $assign, [], false);
57fbd5f9
DW
2879
2880 // Revert to draft so the student can edit it.
757d5b7c 2881 $assign->revert_to_draft($student->id);
57fbd5f9 2882
757d5b7c 2883 $this->setUser($student);
57fbd5f9
DW
2884
2885 // Check that the submission text was saved.
757d5b7c 2886 $output = $assign->view_student_summary($student, true);
57fbd5f9
DW
2887 $this->assertContains('Teacher edited submission text', $output, 'Contains student submission text');
2888
2889 // Check that the student can submit their work.
757d5b7c 2890 $this->submit_for_grading($student, $assign, []);
57fbd5f9 2891
757d5b7c 2892 $output = $assign->view_student_summary($student, true);
57fbd5f9
DW
2893 $this->assertNotContains(get_string('addsubmission', 'assign'), $output);
2894
757d5b7c
AN
2895 // An editing teacher without the extra role should still be able to revert to draft.
2896 $this->setUser($otherteacher);
57fbd5f9
DW
2897
2898 // Revert to draft so the submission is editable.
757d5b7c 2899 $assign->revert_to_draft($student->id);
57fbd5f9
DW
2900 }
2901
757d5b7c
AN
2902 /**
2903 * Ensure that disabling submit after the cutoff date works as expected.
2904 */
9054c04d 2905 public function test_disable_submit_after_cutoff_date() {
2906 global $PAGE;
2907
757d5b7c
AN
2908 $this->resetAfterTest();
2909 $course = $this->getDataGenerator()->create_course();
2910 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
2911
9054c04d 2912 $now = time();
757d5b7c
AN
2913 $tomorrow = $now + DAYSECS;
2914 $lastweek = $now - (7 * DAYSECS);
2915 $yesterday = $now - DAYSECS;
2916
2917 $this->setAdminUser();
2918 $assign = $this->create_instance($course, [
2919 'duedate' => $yesterday,
2920 'cutoffdate' => $tomorrow,
2921 'assignsubmission_onlinetext_enabled' => 1,
2922 ]);
9054c04d 2923
757d5b7c 2924 $PAGE->set_url(new moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id]));
9054c04d 2925
2926 // Student should be able to see an add submission button.
757d5b7c
AN
2927 $this->setUser($student);
2928 $output = $assign->view_student_summary($student, true);
9054c04d 2929 $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign')));
2930
2931 // Add a submission but don't submit now.
757d5b7c 2932 $this->add_submission($student, $assign);
9054c04d 2933
2934 // Create another instance with cut-off and due-date already passed.
757d5b7c
AN
2935 $this->setAdminUser();
2936 $assign = $this->create_instance($course, [
2937 'duedate' => $lastweek,
2938 'cutoffdate' => $yesterday,
2939 'assignsubmission_onlinetext_enabled' => 1,
2940 ]);
9054c04d 2941
757d5b7c
AN
2942 $this->setUser($student);
2943 $output = $assign->view_student_summary($student, true);
9054c04d 2944 $this->assertNotContains($output, get_string('editsubmission', 'assign'),
2945 'Should not be able to edit after cutoff date.');
2946 $this->assertNotContains($output, get_string('submitassignment', 'assign'),
2947 'Should not be able to submit after cutoff date.');
2948 }
757d5b7c 2949
f159ad73 2950 /**
757d5b7c
AN
2951 * Testing for submission comment plugin settings.
2952 *
2953 * @dataProvider submission_plugin_settings_provider
2954 * @param bool $globalenabled
2955 * @param array $instanceconfig
2956 * @param bool $isenabled
f159ad73 2957 */
757d5b7c 2958 public function test_submission_comment_plugin_settings($globalenabled, $instanceconfig, $isenabled) {
f159ad73 2959 global $CFG;
2960
757d5b7c
AN
2961 $this->resetAfterTest();
2962 $course = $this->getDataGenerator()->create_course();
f159ad73 2963
757d5b7c
AN
2964 $CFG->usecomments = $globalenabled;
2965 $assign = $this->create_instance($course, $instanceconfig);
f159ad73 2966 $plugin = $assign->get_submission_plugin_by_type('comments');
757d5b7c
AN
2967 $this->assertEquals($isenabled, (bool) $plugin->is_enabled('enabled'));
2968 }
f159ad73 2969
757d5b7c
AN
2970 public function submission_plugin_settings_provider() {
2971 return [
2972 'CFG->usecomments true, empty config => Enabled by default' => [
2973 true,
2974 [],
2975 true,
2976 ],
2977 'CFG->usecomments true, config enabled => Comments enabled' => [
2978 true,
2979 [
2980 'assignsubmission_comments_enabled' => 1,
2981 ],
2982 true,
2983 ],
2984 'CFG->usecomments true, config idisabled => Comments enabled' => [
2985 true,
2986 [
2987 'assignsubmission_comments_enabled' => 0,
2988 ],
2989 true,
2990 ],
2991 'CFG->usecomments false, empty config => Disabled by default' => [
2992 false,
2993 [],
2994 false,
2995 ],
2996 'CFG->usecomments false, config enabled => Comments disabled' => [
2997 false,
2998 [
2999 'assignsubmission_comments_enabled' => 1,
3000 ],
3001 false,
3002 ],
3003 'CFG->usecomments false, config disabled => Comments disabled' => [
3004 false,
3005 [
3006 'assignsubmission_comments_enabled' => 0,
3007 ],
3008 false,
3009 ],
3010 ];
f159ad73 3011 }
3012
c7a73689
DW
3013 /**
3014 * Testing for comment inline settings
3015 */
3016 public function test_feedback_comment_commentinline() {
36727759 3017 global $CFG, $USER;
c7a73689 3018
757d5b7c
AN
3019 $this->resetAfterTest();
3020 $course = $this->getDataGenerator()->create_course();
3021 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
3022 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
3023
c7a73689
DW
3024 $sourcetext = "Hello!
3025
3026I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
3027
3028URL outside a tag: https://moodle.org/logo/logo-240x60.gif
3029Plugin url outside a tag: @@PLUGINFILE@@/logo-240x60.gif
3030
3031External link 1:<img src='https://moodle.org/logo/logo-240x60.gif' alt='Moodle'/>
3032External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\"/>
3033Internal link 1:<img src='@@PLUGINFILE@@/logo-240x60.gif' alt='Moodle'/>
3034Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\"/>
3035Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\" alt=\"bananas\">Link text</a>
3036Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
c7a73689
DW
3037";
3038
757d5b7c
AN
3039 $this->setUser($teacher);
3040 $assign = $this->create_instance($course, [
3041 'assignsubmission_onlinetext_enabled' => 1,
3042 'assignfeedback_comments_enabled' => 1,
3043 'assignfeedback_comments_commentinline' => 1,
3044 ]);
c7a73689 3045
757d5b7c 3046 $this->setUser($student);
c7a73689 3047
757d5b7c
AN
3048 // Add a submission but don't submit now.
3049 $this->add_submission($student, $assign, $sourcetext);
c7a73689 3050
757d5b7c 3051 $this->setUser($teacher);
c7a73689
DW
3052
3053 $data = new stdClass();
3054 require_once($CFG->dirroot . '/mod/assign/gradeform.php');
757d5b7c
AN
3055 $pagination = [
3056 'userid' => $student->id,
3057 'rownum' => 0,
3058 'last' => true,
3059 'useridlistid' => $assign->get_useridlist_key_id(),
3060 'attemptnumber' => 0,
3061 ];
c7a73689 3062 $formparams = array($assign, $data, $pagination);
757d5b7c 3063 $mform = new mod_assign_grade_form(null, [$assign, $data, $pagination]);
c7a73689 3064
36727759
MN
3065 // We need to get the URL these will be transformed to.
3066 $context = context_user::instance($USER->id);
3067 $itemid = $data->assignfeedbackcomments_editor['itemid'];
3068 $url = $CFG->wwwroot . '/draftfile.php/' . $context->id . '/user/draft/' . $itemid;
3069
3070 // Note the internal images have been stripped and the html is purified (quotes fixed in this case).
3071 $filteredtext = "Hello!
3072
3073I'm writing to you from the Moodle Majlis in Muscat, Oman, where we just had several days of Moodle community goodness.
3074
3075URL outside a tag: https://moodle.org/logo/logo-240x60.gif
3076Plugin url outside a tag: $url/logo-240x60.gif
3077
3078External link 1:<img src=\"https://moodle.org/logo/logo-240x60.gif\" alt=\"Moodle\" />
3079External link 2:<img alt=\"Moodle\" src=\"https://moodle.org/logo/logo-240x60.gif\" />
3080Internal link 1:<img src=\"$url/logo-240x60.gif\" alt=\"Moodle\" />
3081Internal link 2:<img alt=\"Moodle\" src=\"@@PLUGINFILE@@logo-240x60.gif\" />
3082Anchor link 1:<a href=\"@@PLUGINFILE@@logo-240x60.gif\">Link text</a>
3083Anchor link 2:<a title=\"bananas\" href=\"../logo-240x60.gif\">Link text</a>
3084";
3085
c7a73689
DW
3086 $this->assertEquals($filteredtext, $data->assignfeedbackcomments_editor['text']);
3087 }
3088
f159ad73 3089 /**
757d5b7c
AN
3090 * Testing for feedback comment plugin settings.
3091 *
3092 * @dataProvider feedback_plugin_settings_provider
3093 * @param array $instanceconfig
3094 * @param bool $isenabled
f159ad73 3095 */
757d5b7c
AN
3096 public function test_feedback_plugin_settings($instanceconfig, $isenabled) {
3097 $this->resetAfterTest();
3098 $course = $this->getDataGenerator()->create_course();
f159ad73 3099
757d5b7c 3100 $assign = $this->create_instance($course, $instanceconfig);
f159ad73 3101 $plugin = $assign->get_feedback_plugin_by_type('comments');
757d5b7c
AN
3102 $this->assertEquals($isenabled, (bool) $plugin->is_enabled('enabled'));
3103 }
f159ad73 3104
757d5b7c
AN
3105 public function feedback_plugin_settings_provider() {
3106 return [
3107 'No configuration => disabled' => [
3108 [],
3109 false,
3110 ],
3111 'Actively disabled' => [
3112 [
3113 'assignfeedback_comments_enabled' => 0,
3114 ],
3115 false,
3116 ],
3117 'Actively enabled' => [
3118 [
3119 'assignfeedback_comments_enabled' => 1,
3120 ],
3121 true,
3122 ],
3123 ];
f159ad73 3124 }
456d7bc7
RT