Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | 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
 * Choice module library functions tests
19
 *
20
 * @package    mod_choice
21
 * @category   test
22
 * @copyright  2015 Juan Leyva <juan@moodle.com>
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 * @since      Moodle 3.0
25
 */
26
namespace mod_choice;
27
 
28
defined('MOODLE_INTERNAL') || die();
29
 
30
global $CFG;
31
 
32
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
33
require_once($CFG->dirroot . '/mod/choice/lib.php');
34
 
35
/**
36
 * Choice module library functions tests
37
 *
38
 * @package    mod_choice
39
 * @category   test
40
 * @copyright  2015 Juan Leyva <juan@moodle.com>
41
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42
 * @since      Moodle 3.0
43
 */
44
class lib_test extends \externallib_advanced_testcase {
45
 
46
    /**
47
     * Test choice_view
48
     * @return void
49
     */
11 efrain 50
    public function test_choice_view(): void {
1 efrain 51
        global $CFG;
52
 
53
        $this->resetAfterTest();
54
 
55
        $this->setAdminUser();
56
        // Setup test data.
57
        $course = $this->getDataGenerator()->create_course();
58
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id));
59
        $context = \context_module::instance($choice->cmid);
60
        $cm = get_coursemodule_from_instance('choice', $choice->id);
61
 
62
        // Trigger and capture the event.
63
        $sink = $this->redirectEvents();
64
 
65
        choice_view($choice, $course, $cm, $context);
66
 
67
        $events = $sink->get_events();
68
        $this->assertCount(1, $events);
69
        $event = array_shift($events);
70
 
71
        // Checking that the event contains the expected values.
72
        $this->assertInstanceOf('\mod_choice\event\course_module_viewed', $event);
73
        $this->assertEquals($context, $event->get_context());
74
        $url = new \moodle_url('/mod/choice/view.php', array('id' => $cm->id));
75
        $this->assertEquals($url, $event->get_url());
76
        $this->assertEventContextNotUsed($event);
77
        $this->assertNotEmpty($event->get_name());
78
    }
79
 
80
    /**
81
     * Test choice_can_view_results
82
     * @return void
83
     */
11 efrain 84
    public function test_choice_can_view_results(): void {
1 efrain 85
        global $DB, $USER;
86
 
87
        $this->resetAfterTest();
88
 
89
        $this->setAdminUser();
90
        // Setup test data.
91
        $course = $this->getDataGenerator()->create_course();
92
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id));
93
        $context = \context_module::instance($choice->cmid);
94
        $cm = get_coursemodule_from_instance('choice', $choice->id);
95
 
96
        // Default values are false, user cannot view results.
97
        $canview = choice_can_view_results($choice);
98
        $this->assertFalse($canview);
99
 
100
        // Show results forced.
101
        $choice->showresults = CHOICE_SHOWRESULTS_ALWAYS;
102
        $DB->update_record('choice', $choice);
103
        $canview = choice_can_view_results($choice);
104
        $this->assertTrue($canview);
105
 
106
        // Add a time restriction (choice not open yet).
107
        $choice->timeopen = time() + YEARSECS;
108
        $DB->update_record('choice', $choice);
109
        $canview = choice_can_view_results($choice);
110
        $this->assertFalse($canview);
111
 
112
        // Show results after closing.
113
        $choice->timeopen = 0;
114
        $choice->showresults = CHOICE_SHOWRESULTS_AFTER_CLOSE;
115
        $DB->update_record('choice', $choice);
116
        $canview = choice_can_view_results($choice);
117
        $this->assertFalse($canview);
118
 
119
        $choice->timeclose = time() - HOURSECS;
120
        $DB->update_record('choice', $choice);
121
        $canview = choice_can_view_results($choice);
122
        $this->assertTrue($canview);
123
 
124
        // Show results after answering.
125
        $choice->timeclose = 0;
126
        $choice->showresults = CHOICE_SHOWRESULTS_AFTER_ANSWER;
127
        $DB->update_record('choice', $choice);
128
        $canview = choice_can_view_results($choice);
129
        $this->assertFalse($canview);
130
 
131
        // Get the first option.
132
        $choicewithoptions = choice_get_choice($choice->id);
133
        $optionids = array_keys($choicewithoptions->option);
134
 
135
        choice_user_submit_response($optionids[0], $choice, $USER->id, $course, $cm);
136
 
137
        $canview = choice_can_view_results($choice);
138
        $this->assertTrue($canview);
139
 
140
    }
141
 
11 efrain 142
    public function test_choice_user_submit_response_validation(): void {
1 efrain 143
        global $USER;
144
 
145
        $this->resetAfterTest();
146
 
147
        $this->setAdminUser();
148
        // Setup test data.
149
        $course = $this->getDataGenerator()->create_course();
150
        $choice1 = $this->getDataGenerator()->create_module('choice', array('course' => $course->id));
151
        $choice2 = $this->getDataGenerator()->create_module('choice', array('course' => $course->id));
152
        $cm = get_coursemodule_from_instance('choice', $choice1->id);
153
 
154
        $choicewithoptions1 = choice_get_choice($choice1->id);
155
        $choicewithoptions2 = choice_get_choice($choice2->id);
156
        $optionids1 = array_keys($choicewithoptions1->option);
157
        $optionids2 = array_keys($choicewithoptions2->option);
158
 
159
        // Make sure we cannot submit options from a different choice instance.
160
        $this->expectException(\moodle_exception::class);
161
        choice_user_submit_response($optionids2[0], $choice1, $USER->id, $course, $cm);
162
    }
163
 
164
    /**
165
     * Test choice_get_user_response
166
     * @return void
167
     */
11 efrain 168
    public function test_choice_get_user_response(): void {
1 efrain 169
        $this->resetAfterTest();
170
 
171
        $this->setAdminUser();
172
        // Setup test data.
173
        $course = $this->getDataGenerator()->create_course();
174
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
175
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id));
176
        $cm = get_coursemodule_from_instance('choice', $choice->id);
177
 
178
        $choicewithoptions = choice_get_choice($choice->id);
179
        $optionids = array_keys($choicewithoptions->option);
180
 
181
        choice_user_submit_response($optionids[0], $choice, $student->id, $course, $cm);
182
        $responses = choice_get_user_response($choice, $student->id);
183
        $this->assertCount(1, $responses);
184
        $response = array_shift($responses);
185
        $this->assertEquals($optionids[0], $response->optionid);
186
 
187
        // Multiple responses.
188
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 'allowmultiple' => 1));
189
        $cm = get_coursemodule_from_instance('choice', $choice->id);
190
 
191
        $choicewithoptions = choice_get_choice($choice->id);
192
        $optionids = array_keys($choicewithoptions->option);
193
 
194
        // Submit a response with the options reversed.
195
        $selections = $optionids;
196
        rsort($selections);
197
        choice_user_submit_response($selections, $choice, $student->id, $course, $cm);
198
        $responses = choice_get_user_response($choice, $student->id);
199
        $this->assertCount(count($optionids), $responses);
200
        foreach ($responses as $resp) {
201
            $this->assertEquals(array_shift($optionids), $resp->optionid);
202
        }
203
    }
204
 
205
    /**
206
     * Test choice_get_my_response
207
     * @return void
208
     */
11 efrain 209
    public function test_choice_get_my_response(): void {
1 efrain 210
        global $USER;
211
 
212
        $this->resetAfterTest();
213
 
214
        $this->setAdminUser();
215
        // Setup test data.
216
        $course = $this->getDataGenerator()->create_course();
217
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id));
218
        $cm = get_coursemodule_from_instance('choice', $choice->id);
219
 
220
        $choicewithoptions = choice_get_choice($choice->id);
221
        $optionids = array_keys($choicewithoptions->option);
222
 
223
        choice_user_submit_response($optionids[0], $choice, $USER->id, $course, $cm);
224
        $responses = choice_get_my_response($choice);
225
        $this->assertCount(1, $responses);
226
        $response = array_shift($responses);
227
        $this->assertEquals($optionids[0], $response->optionid);
228
 
229
        // Multiple responses.
230
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 'allowmultiple' => 1));
231
        $cm = get_coursemodule_from_instance('choice', $choice->id);
232
 
233
        $choicewithoptions = choice_get_choice($choice->id);
234
        $optionids = array_keys($choicewithoptions->option);
235
 
236
        // Submit a response with the options reversed.
237
        $selections = $optionids;
238
        rsort($selections);
239
        choice_user_submit_response($selections, $choice, $USER->id, $course, $cm);
240
        $responses = choice_get_my_response($choice);
241
        $this->assertCount(count($optionids), $responses);
242
        foreach ($responses as $resp) {
243
            $this->assertEquals(array_shift($optionids), $resp->optionid);
244
        }
245
    }
246
 
247
    /**
248
     * Test choice_get_availability_status
249
     * @return void
250
     */
11 efrain 251
    public function test_choice_get_availability_status(): void {
1 efrain 252
        global $USER;
253
 
254
        $this->resetAfterTest();
255
 
256
        $this->setAdminUser();
257
        // Setup test data.
258
        $course = $this->getDataGenerator()->create_course();
259
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id));
260
 
261
        // No time restrictions and updates allowed.
262
        list($status, $warnings) = choice_get_availability_status($choice, false);
263
        $this->assertEquals(true, $status);
264
        $this->assertCount(0, $warnings);
265
 
266
        // No updates allowed, but haven't answered yet.
267
        $choice->allowupdate = false;
268
        list($status, $warnings) = choice_get_availability_status($choice, false);
269
        $this->assertEquals(true, $status);
270
        $this->assertCount(0, $warnings);
271
 
272
        // No updates allowed and have answered.
273
        $cm = get_coursemodule_from_instance('choice', $choice->id);
274
        $choicewithoptions = choice_get_choice($choice->id);
275
        $optionids = array_keys($choicewithoptions->option);
276
        choice_user_submit_response($optionids[0], $choice, $USER->id, $course, $cm);
277
        list($status, $warnings) = choice_get_availability_status($choice, false);
278
        $this->assertEquals(false, $status);
279
        $this->assertCount(1, $warnings);
280
        $this->assertEquals('choicesaved', array_keys($warnings)[0]);
281
 
282
        $choice->allowupdate = true;
283
 
284
        // With time restrictions, still open.
285
        $choice->timeopen = time() - DAYSECS;
286
        $choice->timeclose = time() + DAYSECS;
287
        list($status, $warnings) = choice_get_availability_status($choice, false);
288
        $this->assertEquals(true, $status);
289
        $this->assertCount(0, $warnings);
290
 
291
        // Choice not open yet.
292
        $choice->timeopen = time() + DAYSECS;
293
        $choice->timeclose = $choice->timeopen + DAYSECS;
294
        list($status, $warnings) = choice_get_availability_status($choice, false);
295
        $this->assertEquals(false, $status);
296
        $this->assertCount(1, $warnings);
297
        $this->assertEquals('notopenyet', array_keys($warnings)[0]);
298
 
299
        // Choice closed.
300
        $choice->timeopen = time() - DAYSECS;
301
        $choice->timeclose = time() - 1;
302
        list($status, $warnings) = choice_get_availability_status($choice, false);
303
        $this->assertEquals(false, $status);
304
        $this->assertCount(1, $warnings);
305
        $this->assertEquals('expired', array_keys($warnings)[0]);
306
    }
307
 
308
    /*
309
     * The choice's event should not be shown to a user when the user cannot view the choice activity at all.
310
     */
11 efrain 311
    public function test_choice_core_calendar_provide_event_action_in_hidden_section(): void {
1 efrain 312
        global $CFG;
313
 
314
        $this->resetAfterTest();
315
 
316
        $this->setAdminUser();
317
 
318
        // Create a course.
319
        $course = $this->getDataGenerator()->create_course();
320
 
321
        // Create a student.
322
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
323
 
324
        // Create a choice.
325
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id,
326
                'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS));
327
 
328
        // Create a calendar event.
329
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN);
330
 
331
        // Set sections 0 as hidden.
332
        set_section_visible($course->id, 0, 0);
333
 
334
        // Now, log out.
335
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
336
        $this->setUser();
337
 
338
        // Create an action factory.
339
        $factory = new \core_calendar\action_factory();
340
 
341
        // Decorate action event for the student.
342
        $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id);
343
 
344
        // Confirm the event is not shown at all.
345
        $this->assertNull($actionevent);
346
    }
347
 
348
    /*
349
     * The choice's event should not be shown to a user who does not have permission to view the choice.
350
     */
11 efrain 351
    public function test_choice_core_calendar_provide_event_action_for_non_user(): void {
1 efrain 352
        global $CFG;
353
 
354
        $this->resetAfterTest();
355
 
356
        $this->setAdminUser();
357
 
358
        // Create a course.
359
        $course = $this->getDataGenerator()->create_course();
360
 
361
        // Create a choice.
362
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id,
363
                'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS));
364
 
365
        // Create a calendar event.
366
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN);
367
 
368
        // Now, log out.
369
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
370
        $this->setUser();
371
 
372
        // Create an action factory.
373
        $factory = new \core_calendar\action_factory();
374
 
375
        // Decorate action event.
376
        $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory);
377
 
378
        // Confirm the event is not shown at all.
379
        $this->assertNull($actionevent);
380
    }
381
 
11 efrain 382
    public function test_choice_core_calendar_provide_event_action_open(): void {
1 efrain 383
        $this->resetAfterTest();
384
 
385
        $this->setAdminUser();
386
 
387
        // Create a course.
388
        $course = $this->getDataGenerator()->create_course();
389
 
390
        // Create a choice.
391
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id,
392
            'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS));
393
 
394
        // Create a calendar event.
395
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN);
396
 
397
        // Create an action factory.
398
        $factory = new \core_calendar\action_factory();
399
 
400
        // Decorate action event.
401
        $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory);
402
 
403
        // Confirm the event was decorated.
404
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
405
        $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name());
406
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
407
        $this->assertEquals(1, $actionevent->get_item_count());
408
        $this->assertTrue($actionevent->is_actionable());
409
    }
410
 
11 efrain 411
    public function test_choice_core_calendar_provide_event_action_open_for_user(): void {
1 efrain 412
        $this->resetAfterTest();
413
 
414
        $this->setAdminUser();
415
 
416
        // Create a course.
417
        $course = $this->getDataGenerator()->create_course();
418
 
419
        // Create a student.
420
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
421
 
422
        // Create a choice.
423
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id,
424
            'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS));
425
 
426
        // Create a calendar event.
427
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN);
428
 
429
        // Create an action factory.
430
        $factory = new \core_calendar\action_factory();
431
 
432
        // Decorate action event for the student.
433
        $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id);
434
 
435
        // Confirm the event was decorated.
436
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
437
        $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name());
438
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
439
        $this->assertEquals(1, $actionevent->get_item_count());
440
        $this->assertTrue($actionevent->is_actionable());
441
    }
442
 
443
    /**
444
     * An event should not have an action if the user has already submitted a response
445
     * to the choice activity.
446
     */
11 efrain 447
    public function test_choice_core_calendar_provide_event_action_already_submitted(): void {
1 efrain 448
        $this->resetAfterTest();
449
 
450
        $this->setAdminUser();
451
 
452
        // Create a course.
453
        $course = $this->getDataGenerator()->create_course();
454
 
455
        // Create a student.
456
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
457
 
458
        // Create a choice.
459
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id,
460
            'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS));
461
        $cm = get_coursemodule_from_instance('choice', $choice->id);
462
 
463
        $choicewithoptions = choice_get_choice($choice->id);
464
        $optionids = array_keys($choicewithoptions->option);
465
 
466
        choice_user_submit_response($optionids[0], $choice, $student->id, $course, $cm);
467
 
468
        // Create a calendar event.
469
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN);
470
 
471
        // Create an action factory.
472
        $factory = new \core_calendar\action_factory();
473
 
474
        $this->setUser($student);
475
 
476
        // Decorate action event.
477
        $action = mod_choice_core_calendar_provide_event_action($event, $factory);
478
 
479
        // Confirm no action was returned if the user has already submitted the
480
        // choice activity.
481
        $this->assertNull($action);
482
    }
483
 
484
    /**
485
     * An event should not have an action if the user has already submitted a response
486
     * to the choice activity.
487
     */
11 efrain 488
    public function test_choice_core_calendar_provide_event_action_already_submitted_for_user(): void {
1 efrain 489
        $this->resetAfterTest();
490
 
491
        $this->setAdminUser();
492
 
493
        // Create a course.
494
        $course = $this->getDataGenerator()->create_course();
495
 
496
        // Create a student.
497
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
498
 
499
        // Create a choice.
500
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id,
501
            'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS));
502
        $cm = get_coursemodule_from_instance('choice', $choice->id);
503
 
504
        $choicewithoptions = choice_get_choice($choice->id);
505
        $optionids = array_keys($choicewithoptions->option);
506
 
507
        choice_user_submit_response($optionids[0], $choice, $student->id, $course, $cm);
508
 
509
        // Create a calendar event.
510
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN);
511
 
512
        // Create an action factory.
513
        $factory = new \core_calendar\action_factory();
514
 
515
        // Decorate action event for the student.
516
        $action = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id);
517
 
518
        // Confirm no action was returned if the user has already submitted the
519
        // choice activity.
520
        $this->assertNull($action);
521
    }
522
 
11 efrain 523
    public function test_choice_core_calendar_provide_event_action_closed(): void {
1 efrain 524
        $this->resetAfterTest();
525
 
526
        $this->setAdminUser();
527
 
528
        // Create a course.
529
        $course = $this->getDataGenerator()->create_course();
530
 
531
        $timeclose = time() - DAYSECS;
532
        // Create a choice.
533
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id,
534
            'timeclose' => $timeclose));
535
 
536
        // Create a calendar event.
537
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN, $timeclose - 1);
538
 
539
        // Create an action factory.
540
        $factory = new \core_calendar\action_factory();
541
 
542
        // Decorate action event.
543
        $action = mod_choice_core_calendar_provide_event_action($event, $factory);
544
 
545
        // Confirm not action was provided for a closed activity.
546
        $this->assertNull($action);
547
    }
548
 
11 efrain 549
    public function test_choice_core_calendar_provide_event_action_closed_for_user(): void {
1 efrain 550
        $this->resetAfterTest();
551
 
552
        $this->setAdminUser();
553
 
554
        // Create a course.
555
        $course = $this->getDataGenerator()->create_course();
556
 
557
        // Create a student.
558
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
559
 
560
        $timeclose = time() - DAYSECS;
561
        // Create a choice.
562
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id,
563
            'timeclose' => $timeclose));
564
 
565
        // Create a calendar event.
566
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN, $timeclose - 1);
567
 
568
        // Create an action factory.
569
        $factory = new \core_calendar\action_factory();
570
 
571
        // Decorate action event for the student.
572
        $action = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id);
573
 
574
        // Confirm not action was provided for a closed activity.
575
        $this->assertNull($action);
576
    }
577
 
11 efrain 578
    public function test_choice_core_calendar_provide_event_action_open_in_future(): void {
1 efrain 579
        $this->resetAfterTest();
580
 
581
        $this->setAdminUser();
582
 
583
        // Create a course.
584
        $course = $this->getDataGenerator()->create_course();
585
 
586
        $timeopen = time() + DAYSECS;
587
        $timeclose = $timeopen + DAYSECS;
588
 
589
        // Create a choice.
590
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id,
591
            'timeopen' => $timeopen, 'timeclose' => $timeclose));
592
 
593
        // Create a calendar event.
594
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN, $timeopen);
595
 
596
        // Create an action factory.
597
        $factory = new \core_calendar\action_factory();
598
 
599
        // Decorate action event.
600
        $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory);
601
 
602
        // Confirm the event was decorated.
603
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
604
        $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name());
605
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
606
        $this->assertEquals(1, $actionevent->get_item_count());
607
        $this->assertFalse($actionevent->is_actionable());
608
    }
609
 
11 efrain 610
    public function test_choice_core_calendar_provide_event_action_open_in_future_for_user(): void {
1 efrain 611
        global $CFG;
612
 
613
        $this->resetAfterTest();
614
 
615
        $this->setAdminUser();
616
 
617
        // Create a course.
618
        $course = $this->getDataGenerator()->create_course();
619
 
620
        // Create a student.
621
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
622
 
623
        $timeopen = time() + DAYSECS;
624
        $timeclose = $timeopen + DAYSECS;
625
 
626
        // Create a choice.
627
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id,
628
            'timeopen' => $timeopen, 'timeclose' => $timeclose));
629
 
630
        // Create a calendar event.
631
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN, $timeopen);
632
 
633
        // Now, log out.
634
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
635
        $this->setUser();
636
 
637
        // Create an action factory.
638
        $factory = new \core_calendar\action_factory();
639
 
640
        // Decorate action event for the student.
641
        $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id);
642
 
643
        // Confirm the event was decorated.
644
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
645
        $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name());
646
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
647
        $this->assertEquals(1, $actionevent->get_item_count());
648
        $this->assertFalse($actionevent->is_actionable());
649
    }
650
 
11 efrain 651
    public function test_choice_core_calendar_provide_event_action_no_time_specified(): void {
1 efrain 652
        $this->resetAfterTest();
653
 
654
        $this->setAdminUser();
655
 
656
        // Create a course.
657
        $course = $this->getDataGenerator()->create_course();
658
 
659
        // Create a choice.
660
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id));
661
 
662
        // Create a calendar event.
663
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN);
664
 
665
        // Create an action factory.
666
        $factory = new \core_calendar\action_factory();
667
 
668
        // Decorate action event.
669
        $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory);
670
 
671
        // Confirm the event was decorated.
672
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
673
        $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name());
674
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
675
        $this->assertEquals(1, $actionevent->get_item_count());
676
        $this->assertTrue($actionevent->is_actionable());
677
    }
678
 
11 efrain 679
    public function test_choice_core_calendar_provide_event_action_no_time_specified_for_user(): void {
1 efrain 680
        global $CFG;
681
 
682
        $this->resetAfterTest();
683
 
684
        $this->setAdminUser();
685
 
686
        // Create a course.
687
        $course = $this->getDataGenerator()->create_course();
688
 
689
        // Create a student.
690
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
691
 
692
        // Create a choice.
693
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id));
694
 
695
        // Create a calendar event.
696
        $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN);
697
 
698
        // Now, log out.
699
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
700
        $this->setUser();
701
 
702
        // Create an action factory.
703
        $factory = new \core_calendar\action_factory();
704
 
705
        // Decorate action event for the student.
706
        $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id);
707
 
708
        // Confirm the event was decorated.
709
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
710
        $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name());
711
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
712
        $this->assertEquals(1, $actionevent->get_item_count());
713
        $this->assertTrue($actionevent->is_actionable());
714
    }
715
 
11 efrain 716
    public function test_choice_core_calendar_provide_event_action_already_completed(): void {
1 efrain 717
        $this->resetAfterTest();
718
        set_config('enablecompletion', 1);
719
        $this->setAdminUser();
720
 
721
        // Create the activity.
722
        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
723
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id),
724
            array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
725
 
726
        // Get some additional data.
727
        $cm = get_coursemodule_from_instance('choice', $choice->id);
728
 
729
        // Create a calendar event.
730
        $event = $this->create_action_event($course->id, $choice->id,
731
            \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
732
 
733
        // Mark the activity as completed.
734
        $completion = new \completion_info($course);
735
        $completion->set_module_viewed($cm);
736
 
737
        // Create an action factory.
738
        $factory = new \core_calendar\action_factory();
739
 
740
        // Decorate action event.
741
        $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory);
742
 
743
        // Ensure result was null.
744
        $this->assertNull($actionevent);
745
    }
746
 
11 efrain 747
    public function test_choice_core_calendar_provide_event_action_already_completed_for_user(): void {
1 efrain 748
        $this->resetAfterTest();
749
        set_config('enablecompletion', 1);
750
        $this->setAdminUser();
751
 
752
        // Create the activity.
753
        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
754
        $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id),
755
            array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
756
 
757
        // Enrol a student in the course.
758
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
759
 
760
        // Get some additional data.
761
        $cm = get_coursemodule_from_instance('choice', $choice->id);
762
 
763
        // Create a calendar event.
764
        $event = $this->create_action_event($course->id, $choice->id,
765
            \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
766
 
767
        // Mark the activity as completed for the student.
768
        $completion = new \completion_info($course);
769
        $completion->set_module_viewed($cm, $student->id);
770
 
771
        // Create an action factory.
772
        $factory = new \core_calendar\action_factory();
773
 
774
        // Decorate action event for the student.
775
        $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id);
776
 
777
        // Ensure result was null.
778
        $this->assertNull($actionevent);
779
    }
780
 
781
    /**
782
     * Creates an action event.
783
     *
784
     * @param int $courseid
785
     * @param int $instanceid The choice id.
786
     * @param string $eventtype The event type. eg. CHOICE_EVENT_TYPE_OPEN.
787
     * @param int|null $timestart The start timestamp for the event
788
     * @return bool|calendar_event
789
     */
790
    private function create_action_event($courseid, $instanceid, $eventtype, $timestart = null) {
791
        $event = new \stdClass();
792
        $event->name = 'Calendar event';
793
        $event->modulename = 'choice';
794
        $event->courseid = $courseid;
795
        $event->instance = $instanceid;
796
        $event->type = CALENDAR_EVENT_TYPE_ACTION;
797
        $event->eventtype = $eventtype;
798
 
799
        if ($timestart) {
800
            $event->timestart = $timestart;
801
        } else {
802
            $event->timestart = time();
803
        }
804
 
805
        return \calendar_event::create($event);
806
    }
807
 
808
    /**
809
     * Test the callback responsible for returning the completion rule descriptions.
810
     * This function should work given either an instance of the module (cm_info), such as when checking the active rules,
811
     * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type.
812
     */
11 efrain 813
    public function test_mod_choice_completion_get_active_rule_descriptions(): void {
1 efrain 814
        $this->resetAfterTest();
815
        $this->setAdminUser();
816
 
817
        // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't.
818
        $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
819
        $choice1 = $this->getDataGenerator()->create_module('choice', [
820
            'course' => $course->id,
821
            'completion' => 2,
822
            'completionsubmit' => 1
823
        ]);
824
        $choice2 = $this->getDataGenerator()->create_module('choice', [
825
            'course' => $course->id,
826
            'completion' => 2,
827
            'completionsubmit' => 0
828
        ]);
829
        $cm1 = \cm_info::create(get_coursemodule_from_instance('choice', $choice1->id));
830
        $cm2 = \cm_info::create(get_coursemodule_from_instance('choice', $choice2->id));
831
 
832
        // Data for the stdClass input type.
833
        // This type of input would occur when checking the default completion rules for an activity type, where we don't have
834
        // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info.
835
        $moddefaults = new \stdClass();
836
        $moddefaults->customdata = ['customcompletionrules' => ['completionsubmit' => 1]];
837
        $moddefaults->completion = 2;
838
 
839
        $activeruledescriptions = [get_string('completionsubmit', 'choice')];
840
        $this->assertEquals(mod_choice_get_completion_active_rule_descriptions($cm1), $activeruledescriptions);
841
        $this->assertEquals(mod_choice_get_completion_active_rule_descriptions($cm2), []);
842
        $this->assertEquals(mod_choice_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions);
843
        $this->assertEquals(mod_choice_get_completion_active_rule_descriptions(new \stdClass()), []);
844
    }
845
 
846
    /**
847
     * An unkown event type should not change the choice instance.
848
     */
11 efrain 849
    public function test_mod_choice_core_calendar_event_timestart_updated_unknown_event(): void {
1 efrain 850
        global $CFG, $DB;
851
        require_once($CFG->dirroot . "/calendar/lib.php");
852
 
853
        $this->resetAfterTest(true);
854
        $this->setAdminUser();
855
        $generator = $this->getDataGenerator();
856
        $course = $generator->create_course();
857
        $choicegenerator = $generator->get_plugin_generator('mod_choice');
858
        $timeopen = time();
859
        $timeclose = $timeopen + DAYSECS;
860
        $choice = $choicegenerator->create_instance(['course' => $course->id]);
861
        $choice->timeopen = $timeopen;
862
        $choice->timeclose = $timeclose;
863
        $DB->update_record('choice', $choice);
864
 
865
        // Create a valid event.
866
        $event = new \calendar_event([
867
            'name' => 'Test event',
868
            'description' => '',
869
            'format' => 1,
870
            'courseid' => $course->id,
871
            'groupid' => 0,
872
            'userid' => 2,
873
            'modulename' => 'choice',
874
            'instance' => $choice->id,
875
            'eventtype' => CHOICE_EVENT_TYPE_OPEN . "SOMETHING ELSE",
876
            'timestart' => 1,
877
            'timeduration' => 86400,
878
            'visible' => 1
879
        ]);
880
 
881
        mod_choice_core_calendar_event_timestart_updated($event, $choice);
882
 
883
        $choice = $DB->get_record('choice', ['id' => $choice->id]);
884
        $this->assertEquals($timeopen, $choice->timeopen);
885
        $this->assertEquals($timeclose, $choice->timeclose);
886
    }
887
 
888
    /**
889
     * A CHOICE_EVENT_TYPE_OPEN event should update the timeopen property of
890
     * the choice activity.
891
     */
11 efrain 892
    public function test_mod_choice_core_calendar_event_timestart_updated_open_event(): void {
1 efrain 893
        global $CFG, $DB;
894
        require_once($CFG->dirroot . "/calendar/lib.php");
895
 
896
        $this->resetAfterTest(true);
897
        $this->setAdminUser();
898
        $generator = $this->getDataGenerator();
899
        $course = $generator->create_course();
900
        $choicegenerator = $generator->get_plugin_generator('mod_choice');
901
        $timeopen = time();
902
        $timeclose = $timeopen + DAYSECS;
903
        $timemodified = 1;
904
        $newtimeopen = $timeopen - DAYSECS;
905
        $choice = $choicegenerator->create_instance(['course' => $course->id]);
906
        $choice->timeopen = $timeopen;
907
        $choice->timeclose = $timeclose;
908
        $choice->timemodified = $timemodified;
909
        $DB->update_record('choice', $choice);
910
 
911
        // Create a valid event.
912
        $event = new \calendar_event([
913
            'name' => 'Test event',
914
            'description' => '',
915
            'format' => 1,
916
            'courseid' => $course->id,
917
            'groupid' => 0,
918
            'userid' => 2,
919
            'modulename' => 'choice',
920
            'instance' => $choice->id,
921
            'eventtype' => CHOICE_EVENT_TYPE_OPEN,
922
            'timestart' => $newtimeopen,
923
            'timeduration' => 86400,
924
            'visible' => 1
925
        ]);
926
 
927
        // Trigger and capture the event when adding a contact.
928
        $sink = $this->redirectEvents();
929
 
930
        mod_choice_core_calendar_event_timestart_updated($event, $choice);
931
 
932
        $triggeredevents = $sink->get_events();
933
        $moduleupdatedevents = array_filter($triggeredevents, function($e) {
934
            return is_a($e, 'core\event\course_module_updated');
935
        });
936
 
937
        $choice = $DB->get_record('choice', ['id' => $choice->id]);
938
        // Ensure the timeopen property matches the event timestart.
939
        $this->assertEquals($newtimeopen, $choice->timeopen);
940
        // Ensure the timeclose isn't changed.
941
        $this->assertEquals($timeclose, $choice->timeclose);
942
        // Ensure the timemodified property has been changed.
943
        $this->assertNotEquals($timemodified, $choice->timemodified);
944
        // Confirm that a module updated event is fired when the module
945
        // is changed.
946
        $this->assertNotEmpty($moduleupdatedevents);
947
    }
948
 
949
    /**
950
     * A CHOICE_EVENT_TYPE_CLOSE event should update the timeclose property of
951
     * the choice activity.
952
     */
11 efrain 953
    public function test_mod_choice_core_calendar_event_timestart_updated_close_event(): void {
1 efrain 954
        global $CFG, $DB;
955
        require_once($CFG->dirroot . "/calendar/lib.php");
956
 
957
        $this->resetAfterTest(true);
958
        $this->setAdminUser();
959
        $generator = $this->getDataGenerator();
960
        $course = $generator->create_course();
961
        $choicegenerator = $generator->get_plugin_generator('mod_choice');
962
        $timeopen = time();
963
        $timeclose = $timeopen + DAYSECS;
964
        $timemodified = 1;
965
        $newtimeclose = $timeclose + DAYSECS;
966
        $choice = $choicegenerator->create_instance(['course' => $course->id]);
967
        $choice->timeopen = $timeopen;
968
        $choice->timeclose = $timeclose;
969
        $choice->timemodified = $timemodified;
970
        $DB->update_record('choice', $choice);
971
 
972
        // Create a valid event.
973
        $event = new \calendar_event([
974
            'name' => 'Test event',
975
            'description' => '',
976
            'format' => 1,
977
            'courseid' => $course->id,
978
            'groupid' => 0,
979
            'userid' => 2,
980
            'modulename' => 'choice',
981
            'instance' => $choice->id,
982
            'eventtype' => CHOICE_EVENT_TYPE_CLOSE,
983
            'timestart' => $newtimeclose,
984
            'timeduration' => 86400,
985
            'visible' => 1
986
        ]);
987
 
988
        // Trigger and capture the event when adding a contact.
989
        $sink = $this->redirectEvents();
990
 
991
        mod_choice_core_calendar_event_timestart_updated($event, $choice);
992
 
993
        $triggeredevents = $sink->get_events();
994
        $moduleupdatedevents = array_filter($triggeredevents, function($e) {
995
            return is_a($e, 'core\event\course_module_updated');
996
        });
997
 
998
        $choice = $DB->get_record('choice', ['id' => $choice->id]);
999
        // Ensure the timeclose property matches the event timestart.
1000
        $this->assertEquals($newtimeclose, $choice->timeclose);
1001
        // Ensure the timeopen isn't changed.
1002
        $this->assertEquals($timeopen, $choice->timeopen);
1003
        // Ensure the timemodified property has been changed.
1004
        $this->assertNotEquals($timemodified, $choice->timemodified);
1005
        // Confirm that a module updated event is fired when the module
1006
        // is changed.
1007
        $this->assertNotEmpty($moduleupdatedevents);
1008
    }
1009
 
1010
    /**
1011
     * An unkown event type should not have any limits
1012
     */
11 efrain 1013
    public function test_mod_choice_core_calendar_get_valid_event_timestart_range_unknown_event(): void {
1 efrain 1014
        global $CFG, $DB;
1015
        require_once($CFG->dirroot . "/calendar/lib.php");
1016
 
1017
        $this->resetAfterTest(true);
1018
        $this->setAdminUser();
1019
        $generator = $this->getDataGenerator();
1020
        $course = $generator->create_course();
1021
        $timeopen = time();
1022
        $timeclose = $timeopen + DAYSECS;
1023
        $choice = new \stdClass();
1024
        $choice->timeopen = $timeopen;
1025
        $choice->timeclose = $timeclose;
1026
 
1027
        // Create a valid event.
1028
        $event = new \calendar_event([
1029
            'name' => 'Test event',
1030
            'description' => '',
1031
            'format' => 1,
1032
            'courseid' => $course->id,
1033
            'groupid' => 0,
1034
            'userid' => 2,
1035
            'modulename' => 'choice',
1036
            'instance' => 1,
1037
            'eventtype' => CHOICE_EVENT_TYPE_OPEN . "SOMETHING ELSE",
1038
            'timestart' => 1,
1039
            'timeduration' => 86400,
1040
            'visible' => 1
1041
        ]);
1042
 
1043
        list ($min, $max) = mod_choice_core_calendar_get_valid_event_timestart_range($event, $choice);
1044
        $this->assertNull($min);
1045
        $this->assertNull($max);
1046
    }
1047
 
1048
    /**
1049
     * The open event should be limited by the choice's timeclose property, if it's set.
1050
     */
11 efrain 1051
    public function test_mod_choice_core_calendar_get_valid_event_timestart_range_open_event(): void {
1 efrain 1052
        global $CFG, $DB;
1053
        require_once($CFG->dirroot . "/calendar/lib.php");
1054
 
1055
        $this->resetAfterTest(true);
1056
        $this->setAdminUser();
1057
        $generator = $this->getDataGenerator();
1058
        $course = $generator->create_course();
1059
        $timeopen = time();
1060
        $timeclose = $timeopen + DAYSECS;
1061
        $choice = new \stdClass();
1062
        $choice->timeopen = $timeopen;
1063
        $choice->timeclose = $timeclose;
1064
 
1065
        // Create a valid event.
1066
        $event = new \calendar_event([
1067
            'name' => 'Test event',
1068
            'description' => '',
1069
            'format' => 1,
1070
            'courseid' => $course->id,
1071
            'groupid' => 0,
1072
            'userid' => 2,
1073
            'modulename' => 'choice',
1074
            'instance' => 1,
1075
            'eventtype' => CHOICE_EVENT_TYPE_OPEN,
1076
            'timestart' => 1,
1077
            'timeduration' => 86400,
1078
            'visible' => 1
1079
        ]);
1080
 
1081
        // The max limit should be bounded by the timeclose value.
1082
        list ($min, $max) = mod_choice_core_calendar_get_valid_event_timestart_range($event, $choice);
1083
 
1084
        $this->assertNull($min);
1085
        $this->assertEquals($timeclose, $max[0]);
1086
 
1087
        // No timeclose value should result in no upper limit.
1088
        $choice->timeclose = 0;
1089
        list ($min, $max) = mod_choice_core_calendar_get_valid_event_timestart_range($event, $choice);
1090
 
1091
        $this->assertNull($min);
1092
        $this->assertNull($max);
1093
    }
1094
 
1095
    /**
1096
     * The close event should be limited by the choice's timeopen property, if it's set.
1097
     */
11 efrain 1098
    public function test_mod_choice_core_calendar_get_valid_event_timestart_range_close_event(): void {
1 efrain 1099
        global $CFG, $DB;
1100
        require_once($CFG->dirroot . "/calendar/lib.php");
1101
 
1102
        $this->resetAfterTest(true);
1103
        $this->setAdminUser();
1104
        $generator = $this->getDataGenerator();
1105
        $course = $generator->create_course();
1106
        $timeopen = time();
1107
        $timeclose = $timeopen + DAYSECS;
1108
        $choice = new \stdClass();
1109
        $choice->timeopen = $timeopen;
1110
        $choice->timeclose = $timeclose;
1111
 
1112
        // Create a valid event.
1113
        $event = new \calendar_event([
1114
            'name' => 'Test event',
1115
            'description' => '',
1116
            'format' => 1,
1117
            'courseid' => $course->id,
1118
            'groupid' => 0,
1119
            'userid' => 2,
1120
            'modulename' => 'choice',
1121
            'instance' => 1,
1122
            'eventtype' => CHOICE_EVENT_TYPE_CLOSE,
1123
            'timestart' => 1,
1124
            'timeduration' => 86400,
1125
            'visible' => 1
1126
        ]);
1127
 
1128
        // The max limit should be bounded by the timeclose value.
1129
        list ($min, $max) = mod_choice_core_calendar_get_valid_event_timestart_range($event, $choice);
1130
 
1131
        $this->assertEquals($timeopen, $min[0]);
1132
        $this->assertNull($max);
1133
 
1134
        // No timeclose value should result in no upper limit.
1135
        $choice->timeopen = 0;
1136
        list ($min, $max) = mod_choice_core_calendar_get_valid_event_timestart_range($event, $choice);
1137
 
1138
        $this->assertNull($min);
1139
        $this->assertNull($max);
1140
    }
1141
 
1142
    /**
1143
     * Test choice_user_submit_response for a choice with specific options.
1144
     * Options:
1145
     * allowmultiple: false
1146
     * limitanswers: false
1147
     */
11 efrain 1148
    public function test_choice_user_submit_response_no_multiple_no_limits(): void {
1 efrain 1149
        global $DB;
1150
        $this->resetAfterTest(true);
1151
 
1152
        $generator = $this->getDataGenerator();
1153
        $course = $generator->create_course();
1154
        $user = $generator->create_user();
1155
        $user2 = $generator->create_user();
1156
 
1157
        // User must be enrolled in the course for choice limits to be honoured properly.
1158
        $role = $DB->get_record('role', ['shortname' => 'student']);
1159
        $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
1160
        $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id);
1161
 
1162
        // Create choice, with updates allowed and a two options both limited to 1 response each.
1163
        $choice = $generator->get_plugin_generator('mod_choice')->create_instance([
1164
            'course' => $course->id,
1165
            'allowupdate' => false,
1166
            'limitanswers' => false,
1167
            'allowmultiple' => false,
1168
            'option' => ['red', 'green'],
1169
        ]);
1170
        $cm = get_coursemodule_from_instance('choice', $choice->id);
1171
 
1172
        // Get the choice, with options and limits included.
1173
        $choicewithoptions = choice_get_choice($choice->id);
1174
        $optionids = array_keys($choicewithoptions->option);
1175
 
1176
        // Now, save an response which includes the first option.
1177
        $this->assertNull(choice_user_submit_response($optionids[0], $choicewithoptions, $user->id, $course, $cm));
1178
 
1179
        // Confirm that saving again without changing the selected option will not throw a 'choice full' exception.
1180
        $this->assertNull(choice_user_submit_response($optionids[1], $choicewithoptions, $user->id, $course, $cm));
1181
 
1182
        // Confirm that saving a response for student 2 including the first option is allowed.
1183
        $this->assertNull(choice_user_submit_response($optionids[0], $choicewithoptions, $user2->id, $course, $cm));
1184
 
1185
        // Confirm that trying to save multiple options results in an exception.
1186
        $this->expectException('moodle_exception');
1187
        choice_user_submit_response([$optionids[1], $optionids[1]], $choicewithoptions, $user->id, $course, $cm);
1188
    }
1189
 
1190
    /**
1191
     * Test choice_user_submit_response for a choice with specific options.
1192
     * Options:
1193
     * allowmultiple: true
1194
     * limitanswers: false
1195
     */
11 efrain 1196
    public function test_choice_user_submit_response_multiples_no_limits(): void {
1 efrain 1197
        global $DB;
1198
        $this->resetAfterTest(true);
1199
 
1200
        $generator = $this->getDataGenerator();
1201
        $course = $generator->create_course();
1202
        $user = $generator->create_user();
1203
        $user2 = $generator->create_user();
1204
 
1205
        // User must be enrolled in the course for choice limits to be honoured properly.
1206
        $role = $DB->get_record('role', ['shortname' => 'student']);
1207
        $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
1208
        $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id);
1209
 
1210
        // Create choice, with updates allowed and a two options both limited to 1 response each.
1211
        $choice = $generator->get_plugin_generator('mod_choice')->create_instance([
1212
            'course' => $course->id,
1213
            'allowupdate' => false,
1214
            'allowmultiple' => true,
1215
            'limitanswers' => false,
1216
            'option' => ['red', 'green'],
1217
        ]);
1218
        $cm = get_coursemodule_from_instance('choice', $choice->id);
1219
 
1220
        // Get the choice, with options and limits included.
1221
        $choicewithoptions = choice_get_choice($choice->id);
1222
        $optionids = array_keys($choicewithoptions->option);
1223
 
1224
        // Save a response which includes the first option only.
1225
        $this->assertNull(choice_user_submit_response([$optionids[0]], $choicewithoptions, $user->id, $course, $cm));
1226
 
1227
        // Confirm that adding an option to the response is allowed.
1228
        $this->assertNull(choice_user_submit_response([$optionids[0], $optionids[1]], $choicewithoptions, $user->id, $course, $cm));
1229
 
1230
        // Confirm that saving a response for student 2 including the first option is allowed.
1231
        $this->assertNull(choice_user_submit_response($optionids[0], $choicewithoptions, $user2->id, $course, $cm));
1232
 
1233
        // Confirm that removing an option from the response is allowed.
1234
        $this->assertNull(choice_user_submit_response([$optionids[0]], $choicewithoptions, $user->id, $course, $cm));
1235
 
1236
        // Confirm that removing all options from the response is not allowed via this method.
1237
        $this->expectException('moodle_exception');
1238
        choice_user_submit_response([], $choicewithoptions, $user->id, $course, $cm);
1239
    }
1240
 
1241
    /**
1242
     * Test choice_user_submit_response for a choice with specific options.
1243
     * Options:
1244
     * allowmultiple: false
1245
     * limitanswers: true
1246
     */
11 efrain 1247
    public function test_choice_user_submit_response_no_multiples_limits(): void {
1 efrain 1248
        global $DB;
1249
        $this->resetAfterTest(true);
1250
 
1251
        $generator = $this->getDataGenerator();
1252
        $course = $generator->create_course();
1253
        $user = $generator->create_user();
1254
        $user2 = $generator->create_user();
1255
 
1256
        // User must be enrolled in the course for choice limits to be honoured properly.
1257
        $role = $DB->get_record('role', ['shortname' => 'student']);
1258
        $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
1259
        $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id);
1260
 
1261
        // Create choice, with updates allowed and a two options both limited to 1 response each.
1262
        $choice = $generator->get_plugin_generator('mod_choice')->create_instance([
1263
            'course' => $course->id,
1264
            'allowupdate' => false,
1265
            'allowmultiple' => false,
1266
            'limitanswers' => true,
1267
            'option' => ['red', 'green'],
1268
            'limit' => [1, 1]
1269
        ]);
1270
        $cm = get_coursemodule_from_instance('choice', $choice->id);
1271
 
1272
        // Get the choice, with options and limits included.
1273
        $choicewithoptions = choice_get_choice($choice->id);
1274
        $optionids = array_keys($choicewithoptions->option);
1275
 
1276
        // Save a response which includes the first option only.
1277
        $this->assertNull(choice_user_submit_response($optionids[0], $choicewithoptions, $user->id, $course, $cm));
1278
 
1279
        // Confirm that changing the option in the response is allowed.
1280
        $this->assertNull(choice_user_submit_response($optionids[1], $choicewithoptions, $user->id, $course, $cm));
1281
 
1282
        // Confirm that limits are respected by trying to save the same option as another user.
1283
        $this->expectException('moodle_exception');
1284
        choice_user_submit_response($optionids[1], $choicewithoptions, $user2->id, $course, $cm);
1285
    }
1286
 
1287
    /**
1288
     * Test choice_user_submit_response for a choice with specific options.
1289
     * Options:
1290
     * allowmultiple: true
1291
     * limitanswers: true
1292
     */
11 efrain 1293
    public function test_choice_user_submit_response_multiples_limits(): void {
1 efrain 1294
        global $DB;
1295
        $this->resetAfterTest(true);
1296
 
1297
        $generator = $this->getDataGenerator();
1298
        $course = $generator->create_course();
1299
        $user = $generator->create_user();
1300
        $user2 = $generator->create_user();
1301
 
1302
        // User must be enrolled in the course for choice limits to be honoured properly.
1303
        $role = $DB->get_record('role', ['shortname' => 'student']);
1304
        $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id);
1305
        $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id);
1306
 
1307
        // Create choice, with updates allowed and a two options both limited to 1 response each.
1308
        $choice = $generator->get_plugin_generator('mod_choice')->create_instance([
1309
            'course' => $course->id,
1310
            'allowupdate' => false,
1311
            'allowmultiple' => true,
1312
            'limitanswers' => true,
1313
            'option' => ['red', 'green'],
1314
            'limit' => [1, 1]
1315
        ]);
1316
        $cm = get_coursemodule_from_instance('choice', $choice->id);
1317
 
1318
        // Get the choice, with options and limits included.
1319
        $choicewithoptions = choice_get_choice($choice->id);
1320
        $optionids = array_keys($choicewithoptions->option);
1321
 
1322
        // Now, save a response which includes the first option only.
1323
        $this->assertNull(choice_user_submit_response([$optionids[0]], $choicewithoptions, $user->id, $course, $cm));
1324
 
1325
        // Confirm that changing the option in the response is allowed.
1326
        $this->assertNull(choice_user_submit_response([$optionids[1]], $choicewithoptions, $user->id, $course, $cm));
1327
 
1328
        // Confirm that adding an option to the response is allowed.
1329
        $this->assertNull(choice_user_submit_response([$optionids[0], $optionids[1]], $choicewithoptions, $user->id, $course, $cm));
1330
 
1331
        // Confirm that limits are respected by trying to save the same option as another user.
1332
        $this->expectException('moodle_exception');
1333
        choice_user_submit_response($optionids[1], $choicewithoptions, $user2->id, $course, $cm);
1334
    }
1335
 
1336
    /**
1337
     * A user who does not have capabilities to add events to the calendar should be able to create an choice.
1338
     */
11 efrain 1339
    public function test_creation_with_no_calendar_capabilities(): void {
1 efrain 1340
        $this->resetAfterTest();
1341
        $course = self::getDataGenerator()->create_course();
1342
        $context = \context_course::instance($course->id);
1343
        $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher');
1344
        $roleid = self::getDataGenerator()->create_role();
1345
        self::getDataGenerator()->role_assign($roleid, $user->id, $context->id);
1346
        assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true);
1347
        $generator = self::getDataGenerator()->get_plugin_generator('mod_choice');
1348
        // Create an instance as a user without the calendar capabilities.
1349
        $this->setUser($user);
1350
        $time = time();
1351
        $params = array(
1352
            'course' => $course->id,
1353
            'timeopen' => $time + 200,
1354
            'timeclose' => $time + 500,
1355
        );
1356
        $generator->create_instance($params);
1357
    }
1358
}