Proyectos de Subversion Moodle

Rev

Ir a la última revisión | | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
 * Feedback module external functions tests
19
 *
20
 * @package    mod_feedback
21
 * @category   external
22
 * @copyright  2017 Juan Leyva <juan@moodle.com>
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 * @since      Moodle 3.3
25
 */
26
 
27
namespace mod_feedback\external;
28
 
29
use core_external\external_api;
30
use externallib_advanced_testcase;
31
use feedback_item_multichoice;
32
use mod_feedback_external;
33
use moodle_exception;
34
 
35
defined('MOODLE_INTERNAL') || die();
36
 
37
global $CFG;
38
 
39
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
40
require_once($CFG->dirroot . '/mod/feedback/lib.php');
41
 
42
/**
43
 * Feedback module external functions tests
44
 *
45
 * @package    mod_feedback
46
 * @category   external
47
 * @copyright  2017 Juan Leyva <juan@moodle.com>
48
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
49
 * @since      Moodle 3.3
50
 * @covers     \mod_feedback_external
51
 */
52
class external_test extends externallib_advanced_testcase {
53
 
54
    // TODO These should be removed.
55
    // Testcase classes should not have any properties or store state.
56
    protected $course;
57
    protected $feedback;
58
    protected $context;
59
    protected $cm;
60
    protected $student;
61
    protected $teacher;
62
    protected $studentrole;
63
    protected $teacherrole;
64
 
65
    /**
66
     * Set up for every test
67
     */
68
    public function setUp(): void {
69
        global $DB;
70
        $this->resetAfterTest();
71
        $this->setAdminUser();
72
 
73
        // Setup test data.
74
        $this->course = $this->getDataGenerator()->create_course();
75
        $this->feedback = $this->getDataGenerator()->create_module('feedback',
76
            array('course' => $this->course->id, 'email_notification' => 1));
77
        $this->context = \context_module::instance($this->feedback->cmid);
78
        $this->cm = get_coursemodule_from_instance('feedback', $this->feedback->id);
79
 
80
        // Create users.
81
        $this->student = self::getDataGenerator()->create_user();
82
        $this->teacher = self::getDataGenerator()->create_user();
83
 
84
        // Users enrolments.
85
        $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
86
        $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
87
        $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual');
88
        $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');
89
    }
90
 
91
    /**
92
     * Helper method to add items to an existing feedback.
93
     *
94
     * @param \stdClass $feedback feedback instance
95
     * @param integer $pagescount the number of pages we want in the feedback
96
     * @return array list of items created
97
     */
98
    public function populate_feedback($feedback, $pagescount = 1) {
99
        $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
100
        $itemscreated = [];
101
 
102
        // Create at least one page.
103
        $itemscreated[] = $feedbackgenerator->create_item_label($feedback);
104
        $itemscreated[] = $feedbackgenerator->create_item_info($feedback);
105
        $itemscreated[] = $feedbackgenerator->create_item_numeric($feedback);
106
 
107
        // Check if we want more pages.
108
        for ($i = 1; $i < $pagescount; $i++) {
109
            $itemscreated[] = $feedbackgenerator->create_item_pagebreak($feedback);
110
            $itemscreated[] = $feedbackgenerator->create_item_multichoice($feedback);
111
            $itemscreated[] = $feedbackgenerator->create_item_multichoicerated($feedback);
112
            $itemscreated[] = $feedbackgenerator->create_item_textarea($feedback);
113
            $itemscreated[] = $feedbackgenerator->create_item_textfield($feedback);
114
            $itemscreated[] = $feedbackgenerator->create_item_numeric($feedback);
115
        }
116
        return $itemscreated;
117
    }
118
 
119
 
120
    /**
121
     * Test test_mod_feedback_get_feedbacks_by_courses
122
     */
123
    public function test_mod_feedback_get_feedbacks_by_courses() {
124
 
125
        // Create additional course.
126
        $course2 = self::getDataGenerator()->create_course();
127
 
128
        // Second feedback.
129
        $record = new \stdClass();
130
        $record->course = $course2->id;
131
        $feedback2 = self::getDataGenerator()->create_module('feedback', $record);
132
 
133
        // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
134
        $enrol = enrol_get_plugin('manual');
135
        $enrolinstances = enrol_get_instances($course2->id, true);
136
        $instance2 = (object) [];
137
        foreach ($enrolinstances as $courseenrolinstance) {
138
            if ($courseenrolinstance->enrol == "manual") {
139
                $instance2 = $courseenrolinstance;
140
                break;
141
            }
142
        }
143
 
144
        $enrol->enrol_user($instance2, $this->student->id, $this->studentrole->id);
145
 
146
        self::setUser($this->student);
147
 
148
        $returndescription = mod_feedback_external::get_feedbacks_by_courses_returns();
149
 
150
        // Create what we expect to be returned when querying the two courses.
151
        // First for the student user.
152
        $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles', 'lang', 'anonymous',
153
            'multiple_submit', 'autonumbering', 'page_after_submitformat', 'publish_stats', 'completionsubmit');
154
 
155
        $properties = feedback_summary_exporter::read_properties_definition();
156
 
157
        // Add expected coursemodule and data.
158
        $feedback1 = $this->feedback;
159
        $feedback1->coursemodule = $feedback1->cmid;
160
        $feedback1->introformat = 1;
161
        $feedback1->introfiles = [];
162
        $feedback1->lang = '';
163
 
164
        $feedback2->coursemodule = $feedback2->cmid;
165
        $feedback2->introformat = 1;
166
        $feedback2->introfiles = [];
167
        $feedback2->lang = '';
168
 
169
        foreach ($expectedfields as $field) {
170
            if (!empty($properties[$field]) && $properties[$field]['type'] == PARAM_BOOL) {
171
                $feedback1->{$field} = (bool) $feedback1->{$field};
172
                $feedback2->{$field} = (bool) $feedback2->{$field};
173
            }
174
            $expected1[$field] = $feedback1->{$field};
175
            $expected2[$field] = $feedback2->{$field};
176
        }
177
 
178
        $expectedfeedbacks = array($expected2, $expected1);
179
 
180
        // Call the external function passing course ids.
181
        $result = mod_feedback_external::get_feedbacks_by_courses(array($course2->id, $this->course->id));
182
        $result = external_api::clean_returnvalue($returndescription, $result);
183
 
184
        $this->assertEquals($expectedfeedbacks, $result['feedbacks']);
185
        $this->assertCount(0, $result['warnings']);
186
 
187
        // Call the external function without passing course id.
188
        $result = mod_feedback_external::get_feedbacks_by_courses();
189
        $result = external_api::clean_returnvalue($returndescription, $result);
190
        $this->assertEquals($expectedfeedbacks, $result['feedbacks']);
191
        $this->assertCount(0, $result['warnings']);
192
 
193
        // Unenrol user from second course and alter expected feedbacks.
194
        $enrol->unenrol_user($instance2, $this->student->id);
195
        array_shift($expectedfeedbacks);
196
 
197
        // Call the external function without passing course id.
198
        $result = mod_feedback_external::get_feedbacks_by_courses();
199
        $result = external_api::clean_returnvalue($returndescription, $result);
200
        $this->assertEquals($expectedfeedbacks, $result['feedbacks']);
201
 
202
        // Call for the second course we unenrolled the user from, expected warning.
203
        $result = mod_feedback_external::get_feedbacks_by_courses(array($course2->id));
204
        $this->assertCount(1, $result['warnings']);
205
        $this->assertEquals('1', $result['warnings'][0]['warningcode']);
206
        $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
207
 
208
        // Now, try as a teacher for getting all the additional fields.
209
        self::setUser($this->teacher);
210
 
211
        $additionalfields = array('email_notification', 'site_after_submit', 'page_after_submit', 'timeopen', 'timeclose',
212
            'timemodified', 'pageaftersubmitfiles');
213
 
214
        $feedback1->pageaftersubmitfiles = [];
215
 
216
        foreach ($additionalfields as $field) {
217
            if (!empty($properties[$field]) && $properties[$field]['type'] == PARAM_BOOL) {
218
                $feedback1->{$field} = (bool) $feedback1->{$field};
219
            }
220
            $expectedfeedbacks[0][$field] = $feedback1->{$field};
221
        }
222
        $expectedfeedbacks[0]['page_after_submitformat'] = 1;
223
 
224
        $result = mod_feedback_external::get_feedbacks_by_courses();
225
        $result = external_api::clean_returnvalue($returndescription, $result);
226
        $this->assertEquals($expectedfeedbacks, $result['feedbacks']);
227
 
228
        // Admin also should get all the information.
229
        self::setAdminUser();
230
 
231
        $result = mod_feedback_external::get_feedbacks_by_courses(array($this->course->id));
232
        $result = external_api::clean_returnvalue($returndescription, $result);
233
        $this->assertEquals($expectedfeedbacks, $result['feedbacks']);
234
    }
235
 
236
    /**
237
     * Test get_feedback_access_information function with basic defaults for student.
238
     */
239
    public function test_get_feedback_access_information_student() {
240
 
241
        self::setUser($this->student);
242
        $result = mod_feedback_external::get_feedback_access_information($this->feedback->id);
243
        $result = external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
244
 
245
        $this->assertFalse($result['canviewanalysis']);
246
        $this->assertFalse($result['candeletesubmissions']);
247
        $this->assertFalse($result['canviewreports']);
248
        $this->assertFalse($result['canedititems']);
249
        $this->assertTrue($result['cancomplete']);
250
        $this->assertTrue($result['cansubmit']);
251
        $this->assertTrue($result['isempty']);
252
        $this->assertTrue($result['isopen']);
253
        $this->assertTrue($result['isanonymous']);
254
        $this->assertFalse($result['isalreadysubmitted']);
255
    }
256
 
257
    /**
258
     * Test get_feedback_access_information function with basic defaults for teacher.
259
     */
260
    public function test_get_feedback_access_information_teacher() {
261
 
262
        self::setUser($this->teacher);
263
        $result = mod_feedback_external::get_feedback_access_information($this->feedback->id);
264
        $result = external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
265
 
266
        $this->assertTrue($result['canviewanalysis']);
267
        $this->assertTrue($result['canviewreports']);
268
        $this->assertTrue($result['canedititems']);
269
        $this->assertTrue($result['candeletesubmissions']);
270
        $this->assertFalse($result['cancomplete']);
271
        $this->assertTrue($result['cansubmit']);
272
        $this->assertTrue($result['isempty']);
273
        $this->assertTrue($result['isopen']);
274
        $this->assertTrue($result['isanonymous']);
275
        $this->assertFalse($result['isalreadysubmitted']);
276
 
277
        // Add some items to the feedback and check is not empty any more.
278
        self::populate_feedback($this->feedback);
279
        $result = mod_feedback_external::get_feedback_access_information($this->feedback->id);
280
        $result = external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
281
        $this->assertFalse($result['isempty']);
282
    }
283
 
284
    /**
285
     * Test view_feedback invalid id.
286
     */
287
    public function test_view_feedback_invalid_id() {
288
        // Test invalid instance id.
289
        $this->expectException(moodle_exception::class);
290
        mod_feedback_external::view_feedback(0);
291
    }
292
    /**
293
     * Test view_feedback not enrolled user.
294
     */
295
    public function test_view_feedback_not_enrolled_user() {
296
        $usernotenrolled = self::getDataGenerator()->create_user();
297
        $this->setUser($usernotenrolled);
298
        $this->expectException(moodle_exception::class);
299
        mod_feedback_external::view_feedback(0);
300
    }
301
    /**
302
     * Test view_feedback no capabilities.
303
     */
304
    public function test_view_feedback_no_capabilities() {
305
        // Test user with no capabilities.
306
        // We need a explicit prohibit since this capability is allowed for students by default.
307
        assign_capability('mod/feedback:view', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
308
        accesslib_clear_all_caches_for_unit_testing();
309
        $this->expectException(moodle_exception::class);
310
        mod_feedback_external::view_feedback(0);
311
    }
312
    /**
313
     * Test view_feedback.
314
     */
315
    public function test_view_feedback() {
316
        // Test user with full capabilities.
317
        $this->setUser($this->student);
318
        // Trigger and capture the event.
319
        $sink = $this->redirectEvents();
320
        $result = mod_feedback_external::view_feedback($this->feedback->id);
321
        $result = external_api::clean_returnvalue(mod_feedback_external::view_feedback_returns(), $result);
322
        $events = $sink->get_events();
323
        $this->assertCount(1, $events);
324
        $event = array_shift($events);
325
        // Checking that the event contains the expected values.
326
        $this->assertInstanceOf('\mod_feedback\event\course_module_viewed', $event);
327
        $this->assertEquals($this->context, $event->get_context());
328
        $moodledata = new \moodle_url('/mod/feedback/view.php', array('id' => $this->cm->id));
329
        $this->assertEquals($moodledata, $event->get_url());
330
        $this->assertEventContextNotUsed($event);
331
        $this->assertNotEmpty($event->get_name());
332
    }
333
 
334
    /**
335
     * Test get_current_completed_tmp.
336
     */
337
    public function test_get_current_completed_tmp() {
338
        global $DB;
339
 
340
        // Force non anonymous.
341
        $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
342
        // Add a completed_tmp record.
343
        $record = [
344
            'feedback' => $this->feedback->id,
345
            'userid' => $this->student->id,
346
            'guestid' => '',
347
            'timemodified' => time() - DAYSECS,
348
            'random_response' => 0,
349
            'anonymous_response' => FEEDBACK_ANONYMOUS_NO,
350
            'courseid' => $this->course->id,
351
        ];
352
        $record['id'] = $DB->insert_record('feedback_completedtmp', (object) $record);
353
 
354
        // Test user with full capabilities.
355
        $this->setUser($this->student);
356
 
357
        $result = mod_feedback_external::get_current_completed_tmp($this->feedback->id);
358
        $result = external_api::clean_returnvalue(mod_feedback_external::get_current_completed_tmp_returns(), $result);
359
        $this->assertEquals($record['id'], $result['feedback']['id']);
360
    }
361
 
362
    /**
363
     * Test get_items.
364
     */
365
    public function test_get_items() {
366
        // Test user with full capabilities.
367
        $this->setUser($this->student);
368
 
369
        // Add questions to the feedback, we are adding 2 pages of questions.
370
        $itemscreated = self::populate_feedback($this->feedback, 2);
371
 
372
        $result = mod_feedback_external::get_items($this->feedback->id);
373
        $result = external_api::clean_returnvalue(mod_feedback_external::get_items_returns(), $result);
374
        $this->assertCount(count($itemscreated), $result['items']);
375
        $index = 1;
376
        foreach ($result['items'] as $key => $item) {
377
            if (is_numeric($itemscreated[$key])) {
378
                continue; // Page break.
379
            }
380
            // Cannot compare directly the exporter and the db object (exporter have more fields).
381
            $this->assertEquals($itemscreated[$key]->id, $item['id']);
382
            $this->assertEquals($itemscreated[$key]->typ, $item['typ']);
383
            $this->assertEquals($itemscreated[$key]->name, $item['name']);
384
            $this->assertEquals($itemscreated[$key]->label, $item['label']);
385
 
386
            if ($item['hasvalue']) {
387
                $this->assertEquals($index, $item['itemnumber']);
388
                $index++;
389
            }
390
        }
391
    }
392
 
393
    /**
394
     * Test get_items, to confirm validation is done too.
395
     *
396
     * @dataProvider items_provider
397
     * @param string $role Whether the current user should be a student or a teacher.
398
     * @param array $info Settings to create the feedback.
399
     * @param string|null $warning The warning message to display or null if warnings result is empty.
400
     */
401
    public function test_get_items_validation(string $role, array $info, ?string $warning): void {
402
        global $DB;
403
 
404
        // Test user with full capabilities.
405
        if ($role === 'teacher') {
406
            $this->setUser($this->teacher);
407
        } else {
408
            $this->setUser($this->student);
409
        }
410
 
411
        // Create the feedback.
412
        $data = ['course' => $this->course->id];
413
        if (array_key_exists('closed', $info) && $info['closed']) {
414
            $data['timeopen'] = time() + DAYSECS;
415
        }
416
        $feedback = $this->getDataGenerator()->create_module('feedback', $data);
417
 
418
        $empty = true;
419
        if (!array_key_exists('empty', $info) || !$info['empty']) {
420
            $empty = false;
421
            /** @var \mod_feedback_generator $feedbackgenerator */
422
            $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
423
            // Add,at least, one item to the feedback.
424
            $feedbackgenerator->create_item_label($feedback);
425
        }
426
 
427
        if (array_key_exists('complete', $info) && !$info['complete']) {
428
            $studentrole = $DB->get_record('role', array('shortname' => 'student'));
429
            $coursecontext = \context_course::instance($this->course->id);
430
            assign_capability('mod/feedback:complete', CAP_PROHIBIT, $studentrole->id, $coursecontext->id);
431
            // Empty all the caches that may be affected by this change.
432
            accesslib_clear_all_caches_for_unit_testing();
433
            \course_modinfo::clear_instance_cache();
434
        }
435
 
436
        $result = mod_feedback_external::get_items($feedback->id);
437
        $result = external_api::clean_returnvalue(mod_feedback_external::get_items_returns(), $result);
438
        if ($warning) {
439
            $this->assertEmpty($result['items']);
440
            $this->assertCount(1, $result['warnings']);
441
            $resultwarning = reset($result['warnings']);
442
            if ($warning == 'required_capability_exception') {
443
                $this->assertStringContainsString('error/nopermission', $resultwarning['message']);
444
            } else {
445
                $this->assertEquals($warning, $resultwarning['message']);
446
            }
447
        } else {
448
            if ($empty) {
449
                $this->assertEmpty($result['items']);
450
            } else {
451
                $this->assertCount(1, $result['items']);
452
            }
453
            $this->assertEmpty($result['warnings']);
454
        }
455
    }
456
 
457
    /**
458
     * Data provider for test_get_items_validation() and test_get_page_items_validation().
459
     *
460
     * @return array
461
     */
462
    public function items_provider(): array {
463
        return [
464
            'Valid feedback (as student)' => [
465
                'role' => 'student',
466
                'info' => [],
467
                'warning' => null,
468
            ],
469
            'Closed feedback (as student)' => [
470
                'role' => 'student',
471
                'info' => ['closed' => true],
472
                'warning' => get_string('feedback_is_not_open', 'feedback'),
473
            ],
474
            'Empty feedback (as student)' => [
475
                'role' => 'student',
476
                'info' => ['empty' => true],
477
                'warning' => get_string('no_items_available_yet', 'feedback'),
478
            ],
479
            'Closed feedback (as student)' => [
480
                'role' => 'student',
481
                'info' => ['closed' => true],
482
                'warning' => get_string('feedback_is_not_open', 'feedback'),
483
            ],
484
            'Cannot complete feedback (as student)' => [
485
                'role' => 'student',
486
                'info' => ['complete' => false],
487
                'warning' => 'required_capability_exception',
488
            ],
489
            'Valid feedback (as teacher)' => [
490
                'role' => 'teacher',
491
                'info' => [],
492
                'warning' => null,
493
            ],
494
            'Closed feedback (as teacher)' => [
495
                'role' => 'teacher',
496
                'info' => ['closed' => true],
497
                'warning' => null,
498
            ],
499
            'Empty feedback (as teacher)' => [
500
                'role' => 'teacher',
501
                'info' => ['empty' => true],
502
                'warning' => null,
503
            ],
504
            'Closed feedback (as teacher)' => [
505
                'role' => 'teacher',
506
                'info' => ['closed' => true],
507
                'warning' => null,
508
            ],
509
            'Cannot complete feedback (as teacher)' => [
510
                'role' => 'teacher',
511
                'info' => ['complete' => false],
512
                'warning' => null,
513
            ],
514
        ];
515
    }
516
 
517
    /**
518
     * Test launch_feedback.
519
     */
520
    public function test_launch_feedback() {
521
        global $DB;
522
 
523
        // Test user with full capabilities.
524
        $this->setUser($this->student);
525
 
526
        // Add questions to the feedback, we are adding 2 pages of questions.
527
        $itemscreated = self::populate_feedback($this->feedback, 2);
528
 
529
        // First try a feedback we didn't attempt.
530
        $result = mod_feedback_external::launch_feedback($this->feedback->id);
531
        $result = external_api::clean_returnvalue(mod_feedback_external::launch_feedback_returns(), $result);
532
        $this->assertEquals(0, $result['gopage']);
533
 
534
        // Now, try a feedback that we attempted.
535
        // Force non anonymous.
536
        $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
537
        // Add a completed_tmp record.
538
        $record = [
539
            'feedback' => $this->feedback->id,
540
            'userid' => $this->student->id,
541
            'guestid' => '',
542
            'timemodified' => time() - DAYSECS,
543
            'random_response' => 0,
544
            'anonymous_response' => FEEDBACK_ANONYMOUS_NO,
545
            'courseid' => $this->course->id,
546
        ];
547
        $record['id'] = $DB->insert_record('feedback_completedtmp', (object) $record);
548
 
549
        // Add a response to the feedback for each question type with possible values.
550
        $response = [
551
            'course_id' => $this->course->id,
552
            'item' => $itemscreated[1]->id, // First item is the info question.
553
            'completed' => $record['id'],
554
            'tmp_completed' => $record['id'],
555
            'value' => 'A',
556
        ];
557
        $DB->insert_record('feedback_valuetmp', (object) $response);
558
        $response = [
559
            'course_id' => $this->course->id,
560
            'item' => $itemscreated[2]->id, // Second item is the numeric question.
561
            'completed' => $record['id'],
562
            'tmp_completed' => $record['id'],
563
            'value' => 5,
564
        ];
565
        $DB->insert_record('feedback_valuetmp', (object) $response);
566
 
567
        $result = mod_feedback_external::launch_feedback($this->feedback->id);
568
        $result = external_api::clean_returnvalue(mod_feedback_external::launch_feedback_returns(), $result);
569
        $this->assertEquals(1, $result['gopage']);
570
    }
571
 
572
    /**
573
     * Test get_page_items.
574
     */
575
    public function test_get_page_items() {
576
        // Test user with full capabilities.
577
        $this->setUser($this->student);
578
 
579
        // Add questions to the feedback, we are adding 2 pages of questions.
580
        $itemscreated = self::populate_feedback($this->feedback, 2);
581
 
582
        // Retrieve first page.
583
        $result = mod_feedback_external::get_page_items($this->feedback->id, 0);
584
        $result = external_api::clean_returnvalue(mod_feedback_external::get_page_items_returns(), $result);
585
        $this->assertCount(3, $result['items']);    // The first page has 3 items.
586
        $this->assertTrue($result['hasnextpage']);
587
        $this->assertFalse($result['hasprevpage']);
588
 
589
        // Retrieve second page.
590
        $result = mod_feedback_external::get_page_items($this->feedback->id, 1);
591
        $result = external_api::clean_returnvalue(mod_feedback_external::get_page_items_returns(), $result);
592
        $this->assertCount(5, $result['items']);    // The second page has 5 items (page break doesn't count).
593
        $this->assertFalse($result['hasnextpage']);
594
        $this->assertTrue($result['hasprevpage']);
595
    }
596
 
597
    /**
598
     * Test get_page_items, to confirm validation is done too.
599
     *
600
     * @dataProvider items_provider
601
     * @param string $role Whether the current user should be a student or a teacher.
602
     * @param array $info Settings to create the feedback.
603
     * @param string|null $warning The warning message to display or null if warnings result is empty.
604
     */
605
    public function test_get_page_items_validation(string $role, array $info, ?string $warning): void {
606
        global $DB;
607
 
608
        // Test user with full capabilities.
609
        if ($role === 'teacher') {
610
            $this->setUser($this->teacher);
611
        } else {
612
            $this->setUser($this->student);
613
        }
614
 
615
        // Create the feedback.
616
        $data = ['course' => $this->course->id];
617
        if (array_key_exists('closed', $info) && $info['closed']) {
618
            $data['timeopen'] = time() + DAYSECS;
619
        }
620
        $feedback = $this->getDataGenerator()->create_module('feedback', $data);
621
 
622
        $empty = true;
623
        if (!array_key_exists('empty', $info) || !$info['empty']) {
624
            $empty = false;
625
            /** @var \mod_feedback_generator $feedbackgenerator */
626
            $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
627
            // Add,at least, one item to the feedback.
628
            $feedbackgenerator->create_item_label($feedback);
629
        }
630
 
631
        if (array_key_exists('complete', $info) && !$info['complete']) {
632
            $studentrole = $DB->get_record('role', array('shortname' => 'student'));
633
            $coursecontext = \context_course::instance($this->course->id);
634
            assign_capability('mod/feedback:complete', CAP_PROHIBIT, $studentrole->id, $coursecontext->id);
635
            // Empty all the caches that may be affected by this change.
636
            accesslib_clear_all_caches_for_unit_testing();
637
            \course_modinfo::clear_instance_cache();
638
        }
639
 
640
        $result = mod_feedback_external::get_page_items($feedback->id, 0);
641
        $result = external_api::clean_returnvalue(mod_feedback_external::get_items_returns(), $result);
642
        if ($warning) {
643
            $this->assertEmpty($result['items']);
644
            $this->assertCount(1, $result['warnings']);
645
            $resultwarning = reset($result['warnings']);
646
            if ($warning == 'required_capability_exception') {
647
                $this->assertStringContainsString('error/nopermission', $resultwarning['message']);
648
            } else {
649
                $this->assertEquals($warning, $resultwarning['message']);
650
            }
651
        } else {
652
            if ($empty) {
653
                $this->assertEmpty($result['items']);
654
            } else {
655
                $this->assertCount(1, $result['items']);
656
            }
657
            $this->assertEmpty($result['warnings']);
658
        }
659
    }
660
 
661
    /**
662
     * Test process_page.
663
     */
664
    public function test_process_page() {
665
        global $DB;
666
 
667
        // Test user with full capabilities.
668
        $this->setUser($this->student);
669
        $pagecontents = 'You finished it!';
670
        $DB->set_field('feedback', 'page_after_submit', $pagecontents, array('id' => $this->feedback->id));
671
 
672
        // Add questions to the feedback, we are adding 2 pages of questions.
673
        $itemscreated = self::populate_feedback($this->feedback, 2);
674
 
675
        $data = [];
676
        foreach ($itemscreated as $item) {
677
 
678
            if (empty($item->hasvalue)) {
679
                continue;
680
            }
681
 
682
            switch ($item->typ) {
683
                case 'textarea':
684
                case 'textfield':
685
                    $value = 'Lorem ipsum';
686
                    break;
687
                case 'numeric':
688
                    $value = 5;
689
                    break;
690
                case 'multichoice':
691
                    $value = '1';
692
                    break;
693
                case 'multichoicerated':
694
                    $value = '1';
695
                    break;
696
                case 'info':
697
                    $value = format_string($this->course->shortname, true, array('context' => $this->context));
698
                    break;
699
                default:
700
                    $value = '';
701
            }
702
            $data[] = ['name' => $item->typ . '_' . $item->id, 'value' => $value];
703
        }
704
 
705
        // Process first page.
706
        $firstpagedata = [$data[0], $data[1]];
707
        $result = mod_feedback_external::process_page($this->feedback->id, 0, $firstpagedata);
708
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
709
        $this->assertEquals(1, $result['jumpto']);
710
        $this->assertFalse($result['completed']);
711
 
712
        // Now, process the second page. But first we are going back to the first page.
713
        $secondpagedata = [$data[2], $data[3], $data[4], $data[5], $data[6]];
714
        $result = mod_feedback_external::process_page($this->feedback->id, 1, $secondpagedata, true);
715
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
716
        $this->assertFalse($result['completed']);
717
        $this->assertEquals(0, $result['jumpto']);  // We jumped to the first page.
718
        // Check the values were correctly saved.
719
        $tmpitems = $DB->get_records('feedback_valuetmp');
720
        $this->assertCount(7, $tmpitems);   // 2 from the first page + 5 from the second page.
721
 
722
        // Go forward again (sending the same data).
723
        $result = mod_feedback_external::process_page($this->feedback->id, 0, $firstpagedata);
724
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
725
        $this->assertEquals(1, $result['jumpto']);
726
        $this->assertFalse($result['completed']);
727
        $tmpitems = $DB->get_records('feedback_valuetmp');
728
        $this->assertCount(7, $tmpitems);   // 2 from the first page + 5 from the second page.
729
 
730
        // And finally, save everything! We are going to modify one previous recorded value.
731
        $messagessink = $this->redirectMessages();
732
        $data[2]['value'] = 2; // 2 is value of the option 'b'.
733
        $secondpagedata = [$data[2], $data[3], $data[4], $data[5], $data[6]];
734
        $result = mod_feedback_external::process_page($this->feedback->id, 1, $secondpagedata);
735
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
736
        $this->assertTrue($result['completed']);
737
        $this->assertTrue(strpos($result['completionpagecontents'], $pagecontents) !== false);
738
        // Check all the items were saved.
739
        $items = $DB->get_records('feedback_value');
740
        $this->assertCount(7, $items);
741
        // Check if the one we modified was correctly saved.
742
        $itemid = $itemscreated[4]->id;
743
        $itemsaved = $DB->get_field('feedback_value', 'value', array('item' => $itemid));
744
        $mcitem = new feedback_item_multichoice();
745
        $itemval = $mcitem->get_printval($itemscreated[4], (object) ['value' => $itemsaved]);
746
        $this->assertEquals('b', $itemval);
747
 
748
        // Check that the answers are saved for course 0.
749
        foreach ($items as $item) {
750
            $this->assertEquals(0, $item->course_id);
751
        }
752
        $completed = $DB->get_record('feedback_completed', []);
753
        $this->assertEquals(0, $completed->courseid);
754
 
755
        // Test notifications sent.
756
        $messages = $messagessink->get_messages();
757
        $messagessink->close();
758
        // Test customdata.
759
        $customdata = json_decode($messages[0]->customdata);
760
        $this->assertEquals($this->feedback->id, $customdata->instance);
761
        $this->assertEquals($this->feedback->cmid, $customdata->cmid);
762
        $this->assertObjectHasProperty('notificationiconurl', $customdata);
763
    }
764
 
765
    /**
766
     * Test process_page for a site feedback.
767
     */
768
    public function test_process_page_site_feedback() {
769
        global $DB;
770
        $pagecontents = 'You finished it!';
771
        $this->feedback = $this->getDataGenerator()->create_module('feedback',
772
            array('course' => SITEID, 'page_after_submit' => $pagecontents));
773
 
774
        // Test user with full capabilities.
775
        $this->setUser($this->student);
776
 
777
        // Add questions to the feedback, we are adding 2 pages of questions.
778
        $itemscreated = self::populate_feedback($this->feedback, 2);
779
 
780
        $data = [];
781
        foreach ($itemscreated as $item) {
782
 
783
            if (empty($item->hasvalue)) {
784
                continue;
785
            }
786
 
787
            switch ($item->typ) {
788
                case 'textarea':
789
                case 'textfield':
790
                    $value = 'Lorem ipsum';
791
                    break;
792
                case 'numeric':
793
                    $value = 5;
794
                    break;
795
                case 'multichoice':
796
                    $value = '1';
797
                    break;
798
                case 'multichoicerated':
799
                    $value = '1';
800
                    break;
801
                case 'info':
802
                    $value = format_string($this->course->shortname, true, array('context' => $this->context));
803
                    break;
804
                default:
805
                    $value = '';
806
            }
807
            $data[] = ['name' => $item->typ . '_' . $item->id, 'value' => $value];
808
        }
809
 
810
        // Process first page.
811
        $firstpagedata = [$data[0], $data[1]];
812
        $result = mod_feedback_external::process_page($this->feedback->id, 0, $firstpagedata, false, $this->course->id);
813
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
814
        $this->assertEquals(1, $result['jumpto']);
815
        $this->assertFalse($result['completed']);
816
 
817
        // Process second page.
818
        $data[2]['value'] = 2; // 2 is value of the option 'b';
819
        $secondpagedata = [$data[2], $data[3], $data[4], $data[5], $data[6]];
820
        $result = mod_feedback_external::process_page($this->feedback->id, 1, $secondpagedata, false, $this->course->id);
821
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
822
        $this->assertTrue($result['completed']);
823
        $this->assertTrue(strpos($result['completionpagecontents'], $pagecontents) !== false);
824
        // Check all the items were saved.
825
        $items = $DB->get_records('feedback_value');
826
        $this->assertCount(7, $items);
827
        // Check if the one we modified was correctly saved.
828
        $itemid = $itemscreated[4]->id;
829
        $itemsaved = $DB->get_field('feedback_value', 'value', array('item' => $itemid));
830
        $mcitem = new feedback_item_multichoice();
831
        $itemval = $mcitem->get_printval($itemscreated[4], (object) ['value' => $itemsaved]);
832
        $this->assertEquals('b', $itemval);
833
 
834
        // Check that the answers are saved for the correct course.
835
        foreach ($items as $item) {
836
            $this->assertEquals($this->course->id, $item->course_id);
837
        }
838
        $completed = $DB->get_record('feedback_completed', []);
839
        $this->assertEquals($this->course->id, $completed->courseid);
840
    }
841
 
842
    /**
843
     * Test get_analysis.
844
     */
845
    public function test_get_analysis() {
846
        // Test user with full capabilities.
847
        $this->setUser($this->student);
848
 
849
        // Create a very simple feedback.
850
        $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
851
        $numericitem = $feedbackgenerator->create_item_numeric($this->feedback);
852
        $textfielditem = $feedbackgenerator->create_item_textfield($this->feedback);
853
 
854
        $pagedata = [
855
            ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 5],
856
            ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'abc'],
857
        ];
858
        // Process the feedback, there is only one page so the feedback will be completed.
859
        $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
860
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
861
        $this->assertTrue($result['completed']);
862
 
863
        // Retrieve analysis.
864
        $this->setUser($this->teacher);
865
        $result = mod_feedback_external::get_analysis($this->feedback->id);
866
        $result = external_api::clean_returnvalue(mod_feedback_external::get_analysis_returns(), $result);
867
        $this->assertEquals(1, $result['completedcount']);  // 1 feedback completed.
868
        $this->assertEquals(2, $result['itemscount']);  // 2 items in the feedback.
869
        $this->assertCount(2, $result['itemsdata']);
870
        $this->assertCount(1, $result['itemsdata'][0]['data']); // There are 1 response per item.
871
        $this->assertCount(1, $result['itemsdata'][1]['data']);
872
        // Check we receive the info the students filled.
873
        foreach ($result['itemsdata'] as $data) {
874
            if ($data['item']['id'] == $numericitem->id) {
875
                $this->assertEquals(5, $data['data'][0]);
876
            } else {
877
                $this->assertEquals('abc', $data['data'][0]);
878
            }
879
        }
880
 
881
        // Create another user / response.
882
        $anotherstudent = self::getDataGenerator()->create_user();
883
        $this->getDataGenerator()->enrol_user($anotherstudent->id, $this->course->id, $this->studentrole->id, 'manual');
884
        $this->setUser($anotherstudent);
885
 
886
        // Process the feedback, there is only one page so the feedback will be completed.
887
        $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
888
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
889
        $this->assertTrue($result['completed']);
890
 
891
        // Retrieve analysis.
892
        $this->setUser($this->teacher);
893
        $result = mod_feedback_external::get_analysis($this->feedback->id);
894
        $result = external_api::clean_returnvalue(mod_feedback_external::get_analysis_returns(), $result);
895
        $this->assertEquals(2, $result['completedcount']);  // 2 feedback completed.
896
        $this->assertEquals(2, $result['itemscount']);
897
        $this->assertCount(2, $result['itemsdata'][0]['data']); // There are 2 responses per item.
898
        $this->assertCount(2, $result['itemsdata'][1]['data']);
899
    }
900
 
901
    /**
902
     * Test get_unfinished_responses.
903
     */
904
    public function test_get_unfinished_responses() {
905
        // Test user with full capabilities.
906
        $this->setUser($this->student);
907
 
908
        // Create a very simple feedback.
909
        $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
910
        $numericitem = $feedbackgenerator->create_item_numeric($this->feedback);
911
        $textfielditem = $feedbackgenerator->create_item_textfield($this->feedback);
912
        $feedbackgenerator->create_item_pagebreak($this->feedback);
913
        $labelitem = $feedbackgenerator->create_item_label($this->feedback);
914
        $numericitem2 = $feedbackgenerator->create_item_numeric($this->feedback);
915
 
916
        $pagedata = [
917
            ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 5],
918
            ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'abc'],
919
        ];
920
        // Process the feedback, there are two pages so the feedback will be unfinished yet.
921
        $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
922
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
923
        $this->assertFalse($result['completed']);
924
 
925
        // Retrieve the unfinished responses.
926
        $result = mod_feedback_external::get_unfinished_responses($this->feedback->id);
927
        $result = external_api::clean_returnvalue(mod_feedback_external::get_unfinished_responses_returns(), $result);
928
        // Check that ids and responses match.
929
        foreach ($result['responses'] as $r) {
930
            if ($r['item'] == $numericitem->id) {
931
                $this->assertEquals(5, $r['value']);
932
            } else {
933
                $this->assertEquals($textfielditem->id, $r['item']);
934
                $this->assertEquals('abc', $r['value']);
935
            }
936
        }
937
    }
938
 
939
    /**
940
     * Test get_finished_responses.
941
     */
942
    public function test_get_finished_responses() {
943
        // Test user with full capabilities.
944
        $this->setUser($this->student);
945
 
946
        // Create a very simple feedback.
947
        $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
948
        $numericitem = $feedbackgenerator->create_item_numeric($this->feedback);
949
        $textfielditem = $feedbackgenerator->create_item_textfield($this->feedback);
950
 
951
        $pagedata = [
952
            ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 5],
953
            ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'abc'],
954
        ];
955
 
956
        // Process the feedback, there is only one page so the feedback will be completed.
957
        $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
958
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
959
        $this->assertTrue($result['completed']);
960
 
961
        // Retrieve the responses.
962
        $result = mod_feedback_external::get_finished_responses($this->feedback->id);
963
        $result = external_api::clean_returnvalue(mod_feedback_external::get_finished_responses_returns(), $result);
964
        // Check that ids and responses match.
965
        foreach ($result['responses'] as $r) {
966
            if ($r['item'] == $numericitem->id) {
967
                $this->assertEquals(5, $r['value']);
968
            } else {
969
                $this->assertEquals($textfielditem->id, $r['item']);
970
                $this->assertEquals('abc', $r['value']);
971
            }
972
        }
973
    }
974
 
975
    /**
976
     * Test get_non_respondents (student trying to get this information).
977
     */
978
    public function test_get_non_respondents_no_permissions() {
979
        $this->setUser($this->student);
980
        $this->expectException(moodle_exception::class);
981
        mod_feedback_external::get_non_respondents($this->feedback->id);
982
    }
983
 
984
    /**
985
     * Test get_non_respondents from an anonymous feedback.
986
     */
987
    public function test_get_non_respondents_from_anonymous_feedback() {
988
        $this->setUser($this->student);
989
        $this->expectException(moodle_exception::class);
990
        $this->expectExceptionMessage(get_string('anonymous', 'feedback'));
991
        mod_feedback_external::get_non_respondents($this->feedback->id);
992
    }
993
 
994
    /**
995
     * Test get_non_respondents.
996
     */
997
    public function test_get_non_respondents() {
998
        global $DB;
999
 
1000
        // Force non anonymous.
1001
        $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
1002
 
1003
        // Create another student.
1004
        $anotherstudent = self::getDataGenerator()->create_user();
1005
        $this->getDataGenerator()->enrol_user($anotherstudent->id, $this->course->id, $this->studentrole->id, 'manual');
1006
        $this->setUser($anotherstudent);
1007
 
1008
        // Test user with full capabilities.
1009
        $this->setUser($this->student);
1010
 
1011
        // Create a very simple feedback.
1012
        $feedbackgenerator = $this->getDataGenerator()->get_plugin_generator('mod_feedback');
1013
        $numericitem = $feedbackgenerator->create_item_numeric($this->feedback);
1014
 
1015
        $pagedata = [
1016
            ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 5],
1017
        ];
1018
 
1019
        // Process the feedback, there is only one page so the feedback will be completed.
1020
        $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
1021
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
1022
        $this->assertTrue($result['completed']);
1023
 
1024
        // Retrieve the non-respondent users.
1025
        $this->setUser($this->teacher);
1026
        $result = mod_feedback_external::get_non_respondents($this->feedback->id);
1027
        $result = external_api::clean_returnvalue(mod_feedback_external::get_non_respondents_returns(), $result);
1028
        $this->assertCount(0, $result['warnings']);
1029
        $this->assertCount(1, $result['users']);
1030
        $this->assertEquals($anotherstudent->id, $result['users'][0]['userid']);
1031
 
1032
        // Create another student.
1033
        $anotherstudent2 = self::getDataGenerator()->create_user();
1034
        $this->getDataGenerator()->enrol_user($anotherstudent2->id, $this->course->id, $this->studentrole->id, 'manual');
1035
        $this->setUser($anotherstudent2);
1036
        $this->setUser($this->teacher);
1037
        $result = mod_feedback_external::get_non_respondents($this->feedback->id);
1038
        $result = external_api::clean_returnvalue(mod_feedback_external::get_non_respondents_returns(), $result);
1039
        $this->assertCount(0, $result['warnings']);
1040
        $this->assertCount(2, $result['users']);
1041
 
1042
        // Test pagination.
1043
        $result = mod_feedback_external::get_non_respondents($this->feedback->id, 0, 'lastaccess', 0, 1);
1044
        $result = external_api::clean_returnvalue(mod_feedback_external::get_non_respondents_returns(), $result);
1045
        $this->assertCount(0, $result['warnings']);
1046
        $this->assertCount(1, $result['users']);
1047
    }
1048
 
1049
    /**
1050
     * Helper function that completes the feedback for two students.
1051
     */
1052
    protected function complete_basic_feedback() {
1053
        global $DB;
1054
 
1055
        $generator = $this->getDataGenerator();
1056
        // Create separated groups.
1057
        $DB->set_field('course', 'groupmode', SEPARATEGROUPS);
1058
        $DB->set_field('course', 'groupmodeforce', 1);
1059
        assign_capability('moodle/site:accessallgroups', CAP_PROHIBIT, $this->teacherrole->id, $this->context);
1060
        accesslib_clear_all_caches_for_unit_testing();
1061
 
1062
        $group1 = $generator->create_group(array('courseid' => $this->course->id));
1063
        $group2 = $generator->create_group(array('courseid' => $this->course->id));
1064
 
1065
        // Create another students.
1066
        $anotherstudent1 = self::getDataGenerator()->create_user();
1067
        $anotherstudent2 = self::getDataGenerator()->create_user();
1068
        $generator->enrol_user($anotherstudent1->id, $this->course->id, $this->studentrole->id, 'manual');
1069
        $generator->enrol_user($anotherstudent2->id, $this->course->id, $this->studentrole->id, 'manual');
1070
 
1071
        $generator->create_group_member(array('groupid' => $group1->id, 'userid' => $this->student->id));
1072
        $generator->create_group_member(array('groupid' => $group1->id, 'userid' => $this->teacher->id));
1073
        $generator->create_group_member(array('groupid' => $group1->id, 'userid' => $anotherstudent1->id));
1074
        $generator->create_group_member(array('groupid' => $group2->id, 'userid' => $anotherstudent2->id));
1075
 
1076
        // Test user with full capabilities.
1077
        $this->setUser($this->student);
1078
 
1079
        // Create a very simple feedback.
1080
        $feedbackgenerator = $generator->get_plugin_generator('mod_feedback');
1081
        $numericitem = $feedbackgenerator->create_item_numeric($this->feedback);
1082
        $textfielditem = $feedbackgenerator->create_item_textfield($this->feedback);
1083
 
1084
        $pagedata = [
1085
            ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 5],
1086
            ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'abc'],
1087
        ];
1088
 
1089
        // Process the feedback, there is only one page so the feedback will be completed.
1090
        $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
1091
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
1092
        $this->assertTrue($result['completed']);
1093
 
1094
        $this->setUser($anotherstudent1);
1095
 
1096
        $pagedata = [
1097
            ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 10],
1098
            ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'def'],
1099
        ];
1100
 
1101
        $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
1102
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
1103
        $this->assertTrue($result['completed']);
1104
 
1105
        $this->setUser($anotherstudent2);
1106
 
1107
        $pagedata = [
1108
            ['name' => $numericitem->typ .'_'. $numericitem->id, 'value' => 10],
1109
            ['name' => $textfielditem->typ .'_'. $textfielditem->id, 'value' => 'def'],
1110
        ];
1111
 
1112
        $result = mod_feedback_external::process_page($this->feedback->id, 0, $pagedata);
1113
        $result = external_api::clean_returnvalue(mod_feedback_external::process_page_returns(), $result);
1114
        $this->assertTrue($result['completed']);
1115
    }
1116
 
1117
    /**
1118
     * Test get_responses_analysis for anonymous feedback.
1119
     */
1120
    public function test_get_responses_analysis_anonymous() {
1121
        self::complete_basic_feedback();
1122
 
1123
        // Retrieve the responses analysis.
1124
        $this->setUser($this->teacher);
1125
        $result = mod_feedback_external::get_responses_analysis($this->feedback->id);
1126
        $result = external_api::clean_returnvalue(mod_feedback_external::get_responses_analysis_returns(), $result);
1127
        $this->assertCount(0, $result['warnings']);
1128
        $this->assertEquals(0, $result['totalattempts']);
1129
        $this->assertEquals(2, $result['totalanonattempts']);   // Only see my groups.
1130
 
1131
        foreach ($result['attempts'] as $attempt) {
1132
            $this->assertEmpty($attempt['userid']); // Is anonymous.
1133
        }
1134
    }
1135
 
1136
    /**
1137
     * Test get_responses_analysis for non-anonymous feedback.
1138
     */
1139
    public function test_get_responses_analysis_non_anonymous() {
1140
        global $DB;
1141
 
1142
        // Force non anonymous.
1143
        $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
1144
 
1145
        self::complete_basic_feedback();
1146
        // Retrieve the responses analysis.
1147
        $this->setUser($this->teacher);
1148
        $result = mod_feedback_external::get_responses_analysis($this->feedback->id);
1149
        $result = external_api::clean_returnvalue(mod_feedback_external::get_responses_analysis_returns(), $result);
1150
        $this->assertCount(0, $result['warnings']);
1151
        $this->assertEquals(2, $result['totalattempts']);
1152
        $this->assertEquals(0, $result['totalanonattempts']);   // Only see my groups.
1153
 
1154
        foreach ($result['attempts'] as $attempt) {
1155
            $this->assertNotEmpty($attempt['userid']);  // Is not anonymous.
1156
        }
1157
    }
1158
 
1159
    /**
1160
     * Test get_last_completed for feedback anonymous not completed.
1161
     */
1162
    public function test_get_last_completed_anonymous_not_completed() {
1163
        global $DB;
1164
 
1165
        // Force anonymous.
1166
        $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_YES, array('id' => $this->feedback->id));
1167
 
1168
        // Test user with full capabilities that didn't complete the feedback.
1169
        $this->setUser($this->student);
1170
 
1171
        $this->expectExceptionMessage(get_string('anonymous', 'feedback'));
1172
        $this->expectException(moodle_exception::class);
1173
        mod_feedback_external::get_last_completed($this->feedback->id);
1174
    }
1175
 
1176
    /**
1177
     * Test get_last_completed for feedback anonymous and completed.
1178
     */
1179
    public function test_get_last_completed_anonymous_completed() {
1180
        global $DB;
1181
 
1182
        // Force anonymous.
1183
        $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_YES, array('id' => $this->feedback->id));
1184
        // Add one completion record..
1185
        $record = [
1186
            'feedback' => $this->feedback->id,
1187
            'userid' => $this->student->id,
1188
            'timemodified' => time() - DAYSECS,
1189
            'random_response' => 0,
1190
            'anonymous_response' => FEEDBACK_ANONYMOUS_YES,
1191
            'courseid' => $this->course->id,
1192
        ];
1193
        $record['id'] = $DB->insert_record('feedback_completed', (object) $record);
1194
 
1195
        // Test user with full capabilities.
1196
        $this->setUser($this->student);
1197
 
1198
        $this->expectExceptionMessage(get_string('anonymous', 'feedback'));
1199
        $this->expectException(moodle_exception::class);
1200
        mod_feedback_external::get_last_completed($this->feedback->id);
1201
    }
1202
 
1203
    /**
1204
     * Test get_last_completed for feedback not anonymous and completed.
1205
     */
1206
    public function test_get_last_completed_not_anonymous_completed() {
1207
        global $DB;
1208
 
1209
        // Force non anonymous.
1210
        $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
1211
        // Add one completion record..
1212
        $record = [
1213
            'feedback' => $this->feedback->id,
1214
            'userid' => $this->student->id,
1215
            'timemodified' => time() - DAYSECS,
1216
            'random_response' => 0,
1217
            'anonymous_response' => FEEDBACK_ANONYMOUS_NO,
1218
            'courseid' => $this->course->id,
1219
        ];
1220
        $record['id'] = $DB->insert_record('feedback_completed', (object) $record);
1221
 
1222
        // Test user with full capabilities.
1223
        $this->setUser($this->student);
1224
        $result = mod_feedback_external::get_last_completed($this->feedback->id);
1225
        $result = external_api::clean_returnvalue(mod_feedback_external::get_last_completed_returns(), $result);
1226
        $this->assertEquals($record, $result['completed']);
1227
    }
1228
 
1229
    /**
1230
     * Test get_last_completed for feedback not anonymous and not completed.
1231
     */
1232
    public function test_get_last_completed_not_anonymous_not_completed() {
1233
        global $DB;
1234
 
1235
        // Force anonymous.
1236
        $DB->set_field('feedback', 'anonymous', FEEDBACK_ANONYMOUS_NO, array('id' => $this->feedback->id));
1237
 
1238
        // Test user with full capabilities that didn't complete the feedback.
1239
        $this->setUser($this->student);
1240
 
1241
        $this->expectExceptionMessage(get_string('not_completed_yet', 'feedback'));
1242
        $this->expectException(moodle_exception::class);
1243
        mod_feedback_external::get_last_completed($this->feedback->id);
1244
    }
1245
 
1246
    /**
1247
     * Test get_feedback_access_information for site feedback.
1248
     */
1249
    public function test_get_feedback_access_information_for_site_feedback() {
1250
 
1251
        $sitefeedback = $this->getDataGenerator()->create_module('feedback', array('course' => SITEID));
1252
        $this->setUser($this->student);
1253
        // Access the site feedback via the site activity.
1254
        $result = mod_feedback_external::get_feedback_access_information($sitefeedback->id);
1255
        $result = external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
1256
        $this->assertTrue($result['cancomplete']);
1257
        $this->assertTrue($result['cansubmit']);
1258
 
1259
        // Access the site feedback via course where I'm enrolled.
1260
        $result = mod_feedback_external::get_feedback_access_information($sitefeedback->id, $this->course->id);
1261
        $result = external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
1262
        $this->assertTrue($result['cancomplete']);
1263
        $this->assertTrue($result['cansubmit']);
1264
 
1265
        // Access the site feedback via course where I'm not enrolled.
1266
        $othercourse = $this->getDataGenerator()->create_course();
1267
 
1268
        $this->expectException(moodle_exception::class);
1269
        mod_feedback_external::get_feedback_access_information($sitefeedback->id, $othercourse->id);
1270
    }
1271
 
1272
    /**
1273
     * Test get_feedback_access_information for site feedback mapped.
1274
     */
1275
    public function test_get_feedback_access_information_for_site_feedback_mapped() {
1276
        global $DB;
1277
 
1278
        $sitefeedback = $this->getDataGenerator()->create_module('feedback', array('course' => SITEID));
1279
        $this->setUser($this->student);
1280
        $DB->insert_record('feedback_sitecourse_map', array('feedbackid' => $sitefeedback->id, 'courseid' => $this->course->id));
1281
 
1282
        // Access the site feedback via course where I'm enrolled and mapped.
1283
        $result = mod_feedback_external::get_feedback_access_information($sitefeedback->id, $this->course->id);
1284
        $result = external_api::clean_returnvalue(mod_feedback_external::get_feedback_access_information_returns(), $result);
1285
        $this->assertTrue($result['cancomplete']);
1286
        $this->assertTrue($result['cansubmit']);
1287
 
1288
        // Access the site feedback via course where I'm enrolled but not mapped.
1289
        $othercourse = $this->getDataGenerator()->create_course();
1290
        $this->getDataGenerator()->enrol_user($this->student->id, $othercourse->id, $this->studentrole->id, 'manual');
1291
 
1292
        $this->expectException(moodle_exception::class);
1293
        $this->expectExceptionMessage(get_string('cannotaccess', 'mod_feedback'));
1294
        mod_feedback_external::get_feedback_access_information($sitefeedback->id, $othercourse->id);
1295
    }
1296
}