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
 * Unit tests for mod/lesson/lib.php.
19
 *
20
 * @package    mod_lesson
21
 * @category   test
22
 * @copyright  2017 Jun Pataleta
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
24
 */
25
namespace mod_lesson;
26
 
27
use lesson;
28
use mod_lesson_external;
29
 
30
defined('MOODLE_INTERNAL') || die();
31
 
32
global $CFG;
33
require_once($CFG->dirroot . '/mod/lesson/lib.php');
34
 
35
/**
36
 * Unit tests for mod/lesson/lib.php.
37
 *
38
 * @copyright  2017 Jun Pataleta
39
 * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
40
 */
41
class lib_test extends \advanced_testcase {
42
    /**
43
     * Test for lesson_get_group_override_priorities().
44
     */
11 efrain 45
    public function test_lesson_get_group_override_priorities(): void {
1 efrain 46
        global $DB;
47
        $this->resetAfterTest();
48
        $this->setAdminUser();
49
 
50
        $dg = $this->getDataGenerator();
51
        $course = $dg->create_course();
52
        $lessonmodule = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
53
 
54
        $this->assertNull(lesson_get_group_override_priorities($lessonmodule->id));
55
 
56
        $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
57
        $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
58
 
59
        $now = 100;
60
        $override1 = (object)[
61
            'lessonid' => $lessonmodule->id,
62
            'groupid' => $group1->id,
63
            'available' => $now,
64
            'deadline' => $now + 20
65
        ];
66
        $DB->insert_record('lesson_overrides', $override1);
67
 
68
        $override2 = (object)[
69
            'lessonid' => $lessonmodule->id,
70
            'groupid' => $group2->id,
71
            'available' => $now - 10,
72
            'deadline' => $now + 10
73
        ];
74
        $DB->insert_record('lesson_overrides', $override2);
75
 
76
        $priorities = lesson_get_group_override_priorities($lessonmodule->id);
77
        $this->assertNotEmpty($priorities);
78
 
79
        $openpriorities = $priorities['open'];
80
        // Override 2's time open has higher priority since it is sooner than override 1's.
81
        $this->assertEquals(2, $openpriorities[$override1->available]);
82
        $this->assertEquals(1, $openpriorities[$override2->available]);
83
 
84
        $closepriorities = $priorities['close'];
85
        // Override 1's time close has higher priority since it is later than override 2's.
86
        $this->assertEquals(1, $closepriorities[$override1->deadline]);
87
        $this->assertEquals(2, $closepriorities[$override2->deadline]);
88
    }
89
 
90
    /**
91
     * Test check_updates_since callback.
92
     */
11 efrain 93
    public function test_check_updates_since(): void {
1 efrain 94
        global $DB;
95
 
96
        $this->resetAfterTest();
97
        $this->setAdminUser();
98
        $course = new \stdClass();
99
        $course->groupmode = SEPARATEGROUPS;
100
        $course->groupmodeforce = true;
101
        $course = $this->getDataGenerator()->create_course($course);
102
 
103
        // Create user.
104
        $studentg1 = self::getDataGenerator()->create_user();
105
        $teacherg1 = self::getDataGenerator()->create_user();
106
        $studentg2 = self::getDataGenerator()->create_user();
107
 
108
        // User enrolment.
109
        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
110
        $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
111
        $this->getDataGenerator()->enrol_user($studentg1->id, $course->id, $studentrole->id, 'manual');
112
        $this->getDataGenerator()->enrol_user($teacherg1->id, $course->id, $teacherrole->id, 'manual');
113
        $this->getDataGenerator()->enrol_user($studentg2->id, $course->id, $studentrole->id, 'manual');
114
 
115
        $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
116
        $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
117
        groups_add_member($group1, $studentg1);
118
        groups_add_member($group2, $studentg2);
119
 
120
        $this->setCurrentTimeStart();
121
        $record = array(
122
            'course' => $course->id,
123
            'custom' => 0,
124
            'feedback' => 1,
125
        );
126
        $lessonmodule = $this->getDataGenerator()->create_module('lesson', $record);
127
        // Convert to a lesson object.
128
        $lesson = new lesson($lessonmodule);
129
        $cm = $lesson->cm;
130
        $cm = \cm_info::create($cm);
131
 
132
        // Check that upon creation, the updates are only about the new configuration created.
133
        $onehourago = time() - HOURSECS;
134
        $updates = lesson_check_updates_since($cm, $onehourago);
135
        foreach ($updates as $el => $val) {
136
            if ($el == 'configuration') {
137
                $this->assertTrue($val->updated);
138
                $this->assertTimeCurrent($val->timeupdated);
139
            } else {
140
                $this->assertFalse($val->updated);
141
            }
142
        }
143
 
144
        // Set up a generator to create content.
145
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
146
        $tfrecord = $generator->create_question_truefalse($lesson);
147
 
148
        // Check now for pages and answers.
149
        $updates = lesson_check_updates_since($cm, $onehourago);
150
        $this->assertTrue($updates->pages->updated);
151
        $this->assertCount(1, $updates->pages->itemids);
152
 
153
        $this->assertTrue($updates->answers->updated);
154
        $this->assertCount(2, $updates->answers->itemids);
155
 
156
        // Now, do something in the lesson with the two users.
157
        $this->setUser($studentg1);
158
        mod_lesson_external::launch_attempt($lesson->id);
159
        $data = array(
160
            array(
161
                'name' => 'answerid',
162
                'value' => $DB->get_field('lesson_answers', 'id', array('pageid' => $tfrecord->id, 'jumpto' => -1)),
163
            ),
164
            array(
165
                'name' => '_qf__lesson_display_answer_form_truefalse',
166
                'value' => 1,
167
            )
168
        );
169
        mod_lesson_external::process_page($lesson->id, $tfrecord->id, $data);
170
        mod_lesson_external::finish_attempt($lesson->id);
171
 
172
        $this->setUser($studentg2);
173
        mod_lesson_external::launch_attempt($lesson->id);
174
        $data = array(
175
            array(
176
                'name' => 'answerid',
177
                'value' => $DB->get_field('lesson_answers', 'id', array('pageid' => $tfrecord->id, 'jumpto' => -1)),
178
            ),
179
            array(
180
                'name' => '_qf__lesson_display_answer_form_truefalse',
181
                'value' => 1,
182
            )
183
        );
184
        mod_lesson_external::process_page($lesson->id, $tfrecord->id, $data);
185
        mod_lesson_external::finish_attempt($lesson->id);
186
 
187
        $this->setUser($studentg1);
188
        $updates = lesson_check_updates_since($cm, $onehourago);
189
 
190
        // Check question attempts, timers and new grades.
191
        $this->assertTrue($updates->questionattempts->updated);
192
        $this->assertCount(1, $updates->questionattempts->itemids);
193
 
194
        $this->assertTrue($updates->grades->updated);
195
        $this->assertCount(1, $updates->grades->itemids);
196
 
197
        $this->assertTrue($updates->timers->updated);
198
        $this->assertCount(1, $updates->timers->itemids);
199
 
200
        // Now, as teacher, check that I can see the two users (even in separate groups).
201
        $this->setUser($teacherg1);
202
        $updates = lesson_check_updates_since($cm, $onehourago);
203
        $this->assertTrue($updates->userquestionattempts->updated);
204
        $this->assertCount(2, $updates->userquestionattempts->itemids);
205
 
206
        $this->assertTrue($updates->usergrades->updated);
207
        $this->assertCount(2, $updates->usergrades->itemids);
208
 
209
        $this->assertTrue($updates->usertimers->updated);
210
        $this->assertCount(2, $updates->usertimers->itemids);
211
 
212
        // Now, teacher can't access all groups.
213
        groups_add_member($group1, $teacherg1);
214
        assign_capability('moodle/site:accessallgroups', CAP_PROHIBIT, $teacherrole->id, \context_module::instance($cm->id));
215
        accesslib_clear_all_caches_for_unit_testing();
216
        $updates = lesson_check_updates_since($cm, $onehourago);
217
        // I will see only the studentg1 updates.
218
        $this->assertTrue($updates->userquestionattempts->updated);
219
        $this->assertCount(1, $updates->userquestionattempts->itemids);
220
 
221
        $this->assertTrue($updates->usergrades->updated);
222
        $this->assertCount(1, $updates->usergrades->itemids);
223
 
224
        $this->assertTrue($updates->usertimers->updated);
225
        $this->assertCount(1, $updates->usertimers->itemids);
226
    }
227
 
11 efrain 228
    public function test_lesson_core_calendar_provide_event_action_open(): void {
1 efrain 229
        $this->resetAfterTest();
230
        $this->setAdminUser();
231
        // Create a course.
232
        $course = $this->getDataGenerator()->create_course();
233
        // Create a teacher and enrol into the course.
234
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
235
        // Create a lesson activity.
236
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id,
237
            'available' => time() - DAYSECS, 'deadline' => time() + DAYSECS));
238
        // Create a calendar event.
239
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
240
 
241
        // Log in as the teacher.
242
        $this->setUser($teacher);
243
        // Create an action factory.
244
        $factory = new \core_calendar\action_factory();
245
        // Decorate action event.
246
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory);
247
        // Confirm the event was decorated.
248
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
249
        $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name());
250
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
251
        $this->assertEquals(1, $actionevent->get_item_count());
252
        $this->assertTrue($actionevent->is_actionable());
253
    }
254
 
11 efrain 255
    public function test_lesson_core_calendar_provide_event_action_open_as_non_user(): void {
1 efrain 256
        global $CFG;
257
 
258
        $this->resetAfterTest();
259
        $this->setAdminUser();
260
 
261
        // Create a course.
262
        $course = $this->getDataGenerator()->create_course();
263
 
264
        // Create a lesson activity.
265
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id,
266
                'available' => time() - DAYSECS, 'deadline' => time() + DAYSECS));
267
 
268
        // Create a calendar event.
269
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
270
 
271
        // Now, log out.
272
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
273
        $this->setUser();
274
 
275
        // Create an action factory.
276
        $factory = new \core_calendar\action_factory();
277
 
278
        // Decorate action event.
279
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory);
280
 
281
        // Confirm the event is not shown at all.
282
        $this->assertNull($actionevent);
283
    }
284
 
11 efrain 285
    public function test_lesson_core_calendar_provide_event_action_open_for_user(): void {
1 efrain 286
        global $CFG;
287
 
288
        $this->resetAfterTest();
289
        $this->setAdminUser();
290
 
291
        // Create a course.
292
        $course = $this->getDataGenerator()->create_course();
293
 
294
        // Create a student.
295
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
296
 
297
        // Create a lesson activity.
298
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id,
299
                'available' => time() - DAYSECS, 'deadline' => time() + DAYSECS));
300
 
301
        // Create a calendar event.
302
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
303
 
304
        // Now, log out.
305
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
306
        $this->setUser();
307
 
308
        // Create an action factory.
309
        $factory = new \core_calendar\action_factory();
310
 
311
        // Decorate action event for the student.
312
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id);
313
 
314
        // Confirm the event was decorated.
315
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
316
        $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name());
317
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
318
        $this->assertEquals(1, $actionevent->get_item_count());
319
        $this->assertTrue($actionevent->is_actionable());
320
    }
321
 
11 efrain 322
    public function test_lesson_core_calendar_provide_event_action_open_in_hidden_section(): void {
1 efrain 323
        $this->resetAfterTest();
324
        $this->setAdminUser();
325
 
326
        // Create a course.
327
        $course = $this->getDataGenerator()->create_course();
328
 
329
        // Create a student.
330
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
331
 
332
        // Create a lesson activity.
333
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id,
334
                'available' => time() - DAYSECS, 'deadline' => time() + DAYSECS));
335
 
336
        // Create a calendar event.
337
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
338
 
339
        // Set sections 0 as hidden.
340
        set_section_visible($course->id, 0, 0);
341
 
342
        // Create an action factory.
343
        $factory = new \core_calendar\action_factory();
344
 
345
        // Decorate action event for the student.
346
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id);
347
 
348
        // Confirm the event is not shown at all.
349
        $this->assertNull($actionevent);
350
    }
351
 
11 efrain 352
    public function test_lesson_core_calendar_provide_event_action_closed(): void {
1 efrain 353
        $this->resetAfterTest();
354
        $this->setAdminUser();
355
 
356
        // Create a course.
357
        $course = $this->getDataGenerator()->create_course();
358
        // Create a teacher and enrol.
359
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
360
 
361
        // Create a lesson activity.
362
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id,
363
            'deadline' => time() - DAYSECS));
364
 
365
        // Create a calendar event.
366
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
367
 
368
        // Now, log in as teacher.
369
        $this->setUser($teacher);
370
        // Create an action factory.
371
        $factory = new \core_calendar\action_factory();
372
 
373
        // Decorate action event.
374
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory);
375
 
376
        // Confirm the event was decorated.
377
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
378
        $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name());
379
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
380
        $this->assertEquals(1, $actionevent->get_item_count());
381
        $this->assertFalse($actionevent->is_actionable());
382
    }
383
 
11 efrain 384
    public function test_lesson_core_calendar_provide_event_action_closed_for_user(): void {
1 efrain 385
        global $CFG;
386
 
387
        $this->resetAfterTest();
388
        $this->setAdminUser();
389
 
390
        // Create a course.
391
        $course = $this->getDataGenerator()->create_course();
392
 
393
        // Create a student.
394
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
395
 
396
        // Create a lesson activity.
397
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id,
398
                'deadline' => time() - DAYSECS));
399
 
400
        // Create a calendar event.
401
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
402
 
403
        // Now, log out.
404
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
405
        $this->setUser();
406
 
407
        // Create an action factory.
408
        $factory = new \core_calendar\action_factory();
409
 
410
        // Decorate action event.
411
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id);
412
 
413
        // Confirm the event was decorated.
414
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
415
        $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name());
416
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
417
        $this->assertEquals(1, $actionevent->get_item_count());
418
        $this->assertFalse($actionevent->is_actionable());
419
    }
420
 
11 efrain 421
    public function test_lesson_core_calendar_provide_event_action_open_in_future(): void {
1 efrain 422
        $this->resetAfterTest();
423
        $this->setAdminUser();
424
 
425
        // Create a course.
426
        $course = $this->getDataGenerator()->create_course();
427
        // Create a teacher and enrol into the course.
428
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
429
        // Create a lesson activity.
430
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id,
431
            'available' => time() + DAYSECS));
432
 
433
        // Create a calendar event.
434
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
435
 
436
        // Now, log in as teacher.
437
        $this->setUser($teacher);
438
        // Create an action factory.
439
        $factory = new \core_calendar\action_factory();
440
 
441
        // Decorate action event.
442
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory);
443
 
444
        // Confirm the event was decorated.
445
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
446
        $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name());
447
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
448
        $this->assertEquals(1, $actionevent->get_item_count());
449
        $this->assertFalse($actionevent->is_actionable());
450
    }
451
 
11 efrain 452
    public function test_lesson_core_calendar_provide_event_action_open_in_future_for_user(): void {
1 efrain 453
        global $CFG;
454
 
455
        $this->resetAfterTest();
456
        $this->setAdminUser();
457
 
458
        // Create a course.
459
        $course = $this->getDataGenerator()->create_course();
460
 
461
        // Create a student.
462
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
463
 
464
        // Create a lesson activity.
465
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id,
466
                'available' => time() + DAYSECS));
467
 
468
        // Create a calendar event.
469
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
470
 
471
        // Now, log out.
472
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
473
        $this->setUser();
474
 
475
        // Create an action factory.
476
        $factory = new \core_calendar\action_factory();
477
 
478
        // Decorate action event.
479
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id);
480
 
481
        // Confirm the event was decorated.
482
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
483
        $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name());
484
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
485
        $this->assertEquals(1, $actionevent->get_item_count());
486
        $this->assertFalse($actionevent->is_actionable());
487
    }
488
 
11 efrain 489
    public function test_lesson_core_calendar_provide_event_action_no_time_specified(): void {
1 efrain 490
        $this->resetAfterTest();
491
        $this->setAdminUser();
492
 
493
        // Create a course.
494
        $course = $this->getDataGenerator()->create_course();
495
        // Create a teacher and enrol into the course.
496
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
497
        // Create a lesson activity.
498
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
499
 
500
        // Create a calendar event.
501
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
502
        // Now, log in as teacher.
503
        $this->setUser($teacher);
504
        // Create an action factory.
505
        $factory = new \core_calendar\action_factory();
506
 
507
        // Decorate action event.
508
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory);
509
 
510
        // Confirm the event was decorated.
511
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
512
        $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name());
513
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
514
        $this->assertEquals(1, $actionevent->get_item_count());
515
        $this->assertTrue($actionevent->is_actionable());
516
    }
517
 
11 efrain 518
    public function test_lesson_core_calendar_provide_event_action_no_time_specified_for_user(): void {
1 efrain 519
        global $CFG;
520
 
521
        $this->resetAfterTest();
522
        $this->setAdminUser();
523
 
524
        // Create a course.
525
        $course = $this->getDataGenerator()->create_course();
526
 
527
        // Create a student.
528
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
529
 
530
        // Create a lesson activity.
531
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
532
 
533
        // Create a calendar event.
534
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
535
 
536
        // Now, log out.
537
        $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities.
538
        $this->setUser();
539
 
540
        // Create an action factory.
541
        $factory = new \core_calendar\action_factory();
542
 
543
        // Decorate action event.
544
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id);
545
 
546
        // Confirm the event was decorated.
547
        $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
548
        $this->assertEquals(get_string('startlesson', 'lesson'), $actionevent->get_name());
549
        $this->assertInstanceOf('moodle_url', $actionevent->get_url());
550
        $this->assertEquals(1, $actionevent->get_item_count());
551
        $this->assertTrue($actionevent->is_actionable());
552
    }
553
 
11 efrain 554
    public function test_lesson_core_calendar_provide_event_action_after_attempt(): void {
1 efrain 555
        global $DB;
556
 
557
        $this->resetAfterTest();
558
        $this->setAdminUser();
559
 
560
        // Create a course.
561
        $course = $this->getDataGenerator()->create_course();
562
 
563
        // Create user.
564
        $student = self::getDataGenerator()->create_user();
565
 
566
        // Create a lesson activity.
567
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
568
 
569
        // Create a calendar event.
570
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
571
 
572
        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
573
        $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
574
 
575
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
576
        $tfrecord = $generator->create_question_truefalse($lesson);
577
 
578
        // Now, do something in the lesson.
579
        $this->setUser($student);
580
        mod_lesson_external::launch_attempt($lesson->id);
581
        $data = array(
582
            array(
583
                'name' => 'answerid',
584
                'value' => $DB->get_field('lesson_answers', 'id', array('pageid' => $tfrecord->id, 'jumpto' => -1)),
585
            ),
586
            array(
587
                'name' => '_qf__lesson_display_answer_form_truefalse',
588
                'value' => 1,
589
            )
590
        );
591
        mod_lesson_external::process_page($lesson->id, $tfrecord->id, $data);
592
        mod_lesson_external::finish_attempt($lesson->id);
593
 
594
        // Create an action factory.
595
        $factory = new \core_calendar\action_factory();
596
 
597
        // Decorate action event.
598
        $action = mod_lesson_core_calendar_provide_event_action($event, $factory);
599
 
600
        // Confirm there was no action for the user.
601
        $this->assertNull($action);
602
    }
603
 
11 efrain 604
    public function test_lesson_core_calendar_provide_event_action_after_attempt_for_user(): void {
1 efrain 605
        global $DB;
606
 
607
        $this->resetAfterTest();
608
        $this->setAdminUser();
609
 
610
        // Create a course.
611
        $course = $this->getDataGenerator()->create_course();
612
 
613
        // Create 2 students in the course.
614
        $student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');
615
        $student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');
616
 
617
        // Create a lesson activity.
618
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id));
619
 
620
        // Create a calendar event.
621
        $event = $this->create_action_event($course->id, $lesson->id, LESSON_EVENT_TYPE_OPEN);
622
 
623
        $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
624
        $tfrecord = $generator->create_question_truefalse($lesson);
625
 
626
        // Now, do something in the lesson as student1.
627
        $this->setUser($student1);
628
        mod_lesson_external::launch_attempt($lesson->id);
629
        $data = array(
630
            array(
631
                'name' => 'answerid',
632
                'value' => $DB->get_field('lesson_answers', 'id', array('pageid' => $tfrecord->id, 'jumpto' => -1)),
633
            ),
634
            array(
635
                'name' => '_qf__lesson_display_answer_form_truefalse',
636
                'value' => 1,
637
            )
638
        );
639
        mod_lesson_external::process_page($lesson->id, $tfrecord->id, $data);
640
        mod_lesson_external::finish_attempt($lesson->id);
641
 
642
        // Now, log in as the other student.
643
        $this->setUser($student2);
644
 
645
        // Create an action factory.
646
        $factory = new \core_calendar\action_factory();
647
 
648
        // Decorate action event.
649
        $action = mod_lesson_core_calendar_provide_event_action($event, $factory, $student1->id);
650
 
651
        // Confirm there was no action for the user.
652
        $this->assertNull($action);
653
    }
654
 
11 efrain 655
    public function test_lesson_core_calendar_provide_event_action_already_completed(): void {
1 efrain 656
        $this->resetAfterTest();
657
        set_config('enablecompletion', 1);
658
        $this->setAdminUser();
659
 
660
        // Create the activity.
661
        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
662
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id),
663
            array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
664
 
665
        // Get some additional data.
666
        $cm = get_coursemodule_from_instance('lesson', $lesson->id);
667
 
668
        // Create a calendar event.
669
        $event = $this->create_action_event($course->id, $lesson->id,
670
            \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
671
 
672
        // Mark the activity as completed.
673
        $completion = new \completion_info($course);
674
        $completion->set_module_viewed($cm);
675
 
676
        // Create an action factory.
677
        $factory = new \core_calendar\action_factory();
678
 
679
        // Decorate action event.
680
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory);
681
 
682
        // Ensure result was null.
683
        $this->assertNull($actionevent);
684
    }
685
 
11 efrain 686
    public function test_lesson_core_calendar_provide_event_action_already_completed_for_user(): void {
1 efrain 687
        $this->resetAfterTest();
688
        set_config('enablecompletion', 1);
689
        $this->setAdminUser();
690
 
691
        // Create the activity.
692
        $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
693
        $lesson = $this->getDataGenerator()->create_module('lesson', array('course' => $course->id),
694
            array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
695
 
696
        // Enrol a student in the course.
697
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
698
 
699
        // Get some additional data.
700
        $cm = get_coursemodule_from_instance('lesson', $lesson->id);
701
 
702
        // Create a calendar event.
703
        $event = $this->create_action_event($course->id, $lesson->id,
704
            \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
705
 
706
        // Mark the activity as completed for the student.
707
        $completion = new \completion_info($course);
708
        $completion->set_module_viewed($cm, $student->id);
709
 
710
        // Create an action factory.
711
        $factory = new \core_calendar\action_factory();
712
 
713
        // Decorate action event for the student.
714
        $actionevent = mod_lesson_core_calendar_provide_event_action($event, $factory, $student->id);
715
 
716
        // Ensure result was null.
717
        $this->assertNull($actionevent);
718
    }
719
 
720
    /**
721
     * Creates an action event.
722
     *
723
     * @param int $courseid
724
     * @param int $instanceid The lesson id.
725
     * @param string $eventtype The event type. eg. LESSON_EVENT_TYPE_OPEN.
726
     * @return bool|calendar_event
727
     */
728
    private function create_action_event($courseid, $instanceid, $eventtype) {
729
        $event = new \stdClass();
730
        $event->name = 'Calendar event';
731
        $event->modulename  = 'lesson';
732
        $event->courseid = $courseid;
733
        $event->instance = $instanceid;
734
        $event->type = CALENDAR_EVENT_TYPE_ACTION;
735
        $event->eventtype = $eventtype;
736
        $event->timestart = time();
737
        return \calendar_event::create($event);
738
    }
739
 
740
    /**
741
     * Test the callback responsible for returning the completion rule descriptions.
742
     * This function should work given either an instance of the module (cm_info), such as when checking the active rules,
743
     * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type.
744
     */
11 efrain 745
    public function test_mod_lesson_completion_get_active_rule_descriptions(): void {
1 efrain 746
        $this->resetAfterTest();
747
        $this->setAdminUser();
748
 
749
        // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't.
750
        $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]);
751
        $lesson1 = $this->getDataGenerator()->create_module('lesson', [
752
            'course' => $course->id,
753
            'completion' => 2,
754
            'completionendreached' => 1,
755
            'completiontimespent' => 3600
756
        ]);
757
        $lesson2 = $this->getDataGenerator()->create_module('lesson', [
758
            'course' => $course->id,
759
            'completion' => 2,
760
            'completionendreached' => 0,
761
            'completiontimespent' => 0
762
        ]);
763
        $cm1 = \cm_info::create(get_coursemodule_from_instance('lesson', $lesson1->id));
764
        $cm2 = \cm_info::create(get_coursemodule_from_instance('lesson', $lesson2->id));
765
 
766
        // Data for the stdClass input type.
767
        // This type of input would occur when checking the default completion rules for an activity type, where we don't have
768
        // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info.
769
        $moddefaults = new \stdClass();
770
        $moddefaults->customdata = ['customcompletionrules' => [
771
            'completionendreached' => 1,
772
            'completiontimespent' => 3600
773
        ]];
774
        $moddefaults->completion = 2;
775
 
776
        $activeruledescriptions = [
777
            get_string('completionendreached_desc', 'lesson'),
778
            get_string('completiontimespentdesc', 'lesson', format_time(3600)),
779
        ];
780
        $this->assertEquals(mod_lesson_get_completion_active_rule_descriptions($cm1), $activeruledescriptions);
781
        $this->assertEquals(mod_lesson_get_completion_active_rule_descriptions($cm2), []);
782
        $this->assertEquals(mod_lesson_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions);
783
        $this->assertEquals(mod_lesson_get_completion_active_rule_descriptions(new \stdClass()), []);
784
    }
785
 
786
    /**
787
     * An unknown event type should not change the lesson instance.
788
     */
11 efrain 789
    public function test_mod_lesson_core_calendar_event_timestart_updated_unknown_event(): void {
1 efrain 790
        global $CFG, $DB;
791
        require_once($CFG->dirroot . "/calendar/lib.php");
792
 
793
        $this->resetAfterTest(true);
794
        $this->setAdminUser();
795
        $generator = $this->getDataGenerator();
796
        $course = $generator->create_course();
797
        $lessongenerator = $generator->get_plugin_generator('mod_lesson');
798
        $timeopen = time();
799
        $timeclose = $timeopen + DAYSECS;
800
        $lesson = $lessongenerator->create_instance(['course' => $course->id]);
801
        $lesson->available = $timeopen;
802
        $lesson->deadline = $timeclose;
803
        $DB->update_record('lesson', $lesson);
804
 
805
        // Create a valid event.
806
        $event = new \calendar_event([
807
            'name' => 'Test event',
808
            'description' => '',
809
            'format' => 1,
810
            'courseid' => $course->id,
811
            'groupid' => 0,
812
            'userid' => 2,
813
            'modulename' => 'lesson',
814
            'instance' => $lesson->id,
815
            'eventtype' => LESSON_EVENT_TYPE_OPEN . "SOMETHING ELSE",
816
            'timestart' => 1,
817
            'timeduration' => 86400,
818
            'visible' => 1
819
        ]);
820
 
821
        mod_lesson_core_calendar_event_timestart_updated($event, $lesson);
822
        $lesson = $DB->get_record('lesson', ['id' => $lesson->id]);
823
        $this->assertEquals($timeopen, $lesson->available);
824
        $this->assertEquals($timeclose, $lesson->deadline);
825
    }
826
 
827
    /**
828
     * A LESSON_EVENT_TYPE_OPEN event should update the available property of the lesson activity.
829
     */
11 efrain 830
    public function test_mod_lesson_core_calendar_event_timestart_updated_open_event(): void {
1 efrain 831
        global $CFG, $DB;
832
        require_once($CFG->dirroot . "/calendar/lib.php");
833
 
834
        $this->resetAfterTest(true);
835
        $this->setAdminUser();
836
        $generator = $this->getDataGenerator();
837
        $course = $generator->create_course();
838
        $lessongenerator = $generator->get_plugin_generator('mod_lesson');
839
        $timeopen = time();
840
        $timeclose = $timeopen + DAYSECS;
841
        $timemodified = 1;
842
        $newtimeopen = $timeopen - DAYSECS;
843
        $lesson = $lessongenerator->create_instance(['course' => $course->id]);
844
        $lesson->available = $timeopen;
845
        $lesson->deadline = $timeclose;
846
        $lesson->timemodified = $timemodified;
847
        $DB->update_record('lesson', $lesson);
848
 
849
        // Create a valid event.
850
        $event = new \calendar_event([
851
            'name' => 'Test event',
852
            'description' => '',
853
            'format' => 1,
854
            'courseid' => $course->id,
855
            'groupid' => 0,
856
            'userid' => 2,
857
            'modulename' => 'lesson',
858
            'instance' => $lesson->id,
859
            'eventtype' => LESSON_EVENT_TYPE_OPEN,
860
            'timestart' => $newtimeopen,
861
            'timeduration' => 86400,
862
            'visible' => 1
863
        ]);
864
 
865
        // Trigger and capture the event when adding a contact.
866
        $sink = $this->redirectEvents();
867
        mod_lesson_core_calendar_event_timestart_updated($event, $lesson);
868
        $triggeredevents = $sink->get_events();
869
        $moduleupdatedevents = array_filter($triggeredevents, function($e) {
870
            return is_a($e, 'core\event\course_module_updated');
871
        });
872
        $lesson = $DB->get_record('lesson', ['id' => $lesson->id]);
873
 
874
        // Ensure the available property matches the event timestart.
875
        $this->assertEquals($newtimeopen, $lesson->available);
876
 
877
        // Ensure the deadline isn't changed.
878
        $this->assertEquals($timeclose, $lesson->deadline);
879
 
880
        // Ensure the timemodified property has been changed.
881
        $this->assertNotEquals($timemodified, $lesson->timemodified);
882
 
883
        // Confirm that a module updated event is fired when the module is changed.
884
        $this->assertNotEmpty($moduleupdatedevents);
885
    }
886
 
887
    /**
888
     * A LESSON_EVENT_TYPE_CLOSE event should update the deadline property of the lesson activity.
889
     */
11 efrain 890
    public function test_mod_lesson_core_calendar_event_timestart_updated_close_event(): void {
1 efrain 891
        global $CFG, $DB;
892
        require_once($CFG->dirroot . "/calendar/lib.php");
893
        $this->resetAfterTest(true);
894
        $this->setAdminUser();
895
        $generator = $this->getDataGenerator();
896
        $course = $generator->create_course();
897
        $lessongenerator = $generator->get_plugin_generator('mod_lesson');
898
        $timeopen = time();
899
        $timeclose = $timeopen + DAYSECS;
900
        $timemodified = 1;
901
        $newtimeclose = $timeclose + DAYSECS;
902
        $lesson = $lessongenerator->create_instance(['course' => $course->id]);
903
        $lesson->available = $timeopen;
904
        $lesson->deadline = $timeclose;
905
        $lesson->timemodified = $timemodified;
906
        $DB->update_record('lesson', $lesson);
907
        // Create a valid event.
908
        $event = new \calendar_event([
909
            'name' => 'Test event',
910
            'description' => '',
911
            'format' => 1,
912
            'courseid' => $course->id,
913
            'groupid' => 0,
914
            'userid' => 2,
915
            'modulename' => 'lesson',
916
            'instance' => $lesson->id,
917
            'eventtype' => LESSON_EVENT_TYPE_CLOSE,
918
            'timestart' => $newtimeclose,
919
            'timeduration' => 86400,
920
            'visible' => 1
921
        ]);
922
        // Trigger and capture the event when adding a contact.
923
        $sink = $this->redirectEvents();
924
        mod_lesson_core_calendar_event_timestart_updated($event, $lesson);
925
        $triggeredevents = $sink->get_events();
926
        $moduleupdatedevents = array_filter($triggeredevents, function($e) {
927
            return is_a($e, 'core\event\course_module_updated');
928
        });
929
        $lesson = $DB->get_record('lesson', ['id' => $lesson->id]);
930
        // Ensure the deadline property matches the event timestart.
931
        $this->assertEquals($newtimeclose, $lesson->deadline);
932
        // Ensure the available isn't changed.
933
        $this->assertEquals($timeopen, $lesson->available);
934
        // Ensure the timemodified property has been changed.
935
        $this->assertNotEquals($timemodified, $lesson->timemodified);
936
        // Confirm that a module updated event is fired when the module is changed.
937
        $this->assertNotEmpty($moduleupdatedevents);
938
    }
939
 
940
    /**
941
     * An unknown event type should not have any limits.
942
     */
11 efrain 943
    public function test_mod_lesson_core_calendar_get_valid_event_timestart_range_unknown_event(): void {
1 efrain 944
        global $CFG;
945
        require_once($CFG->dirroot . "/calendar/lib.php");
946
 
947
        $this->resetAfterTest(true);
948
        $this->setAdminUser();
949
        $generator = $this->getDataGenerator();
950
        $course = $generator->create_course();
951
        $timeopen = time();
952
        $timeclose = $timeopen + DAYSECS;
953
        $lesson = new \stdClass();
954
        $lesson->available = $timeopen;
955
        $lesson->deadline = $timeclose;
956
 
957
        // Create a valid event.
958
        $event = new \calendar_event([
959
            'name' => 'Test event',
960
            'description' => '',
961
            'format' => 1,
962
            'courseid' => $course->id,
963
            'groupid' => 0,
964
            'userid' => 2,
965
            'modulename' => 'lesson',
966
            'instance' => 1,
967
            'eventtype' => LESSON_EVENT_TYPE_OPEN . "SOMETHING ELSE",
968
            'timestart' => 1,
969
            'timeduration' => 86400,
970
            'visible' => 1
971
        ]);
972
 
973
        list ($min, $max) = mod_lesson_core_calendar_get_valid_event_timestart_range($event, $lesson);
974
        $this->assertNull($min);
975
        $this->assertNull($max);
976
    }
977
 
978
    /**
979
     * The open event should be limited by the lesson's deadline property, if it's set.
980
     */
11 efrain 981
    public function test_mod_lesson_core_calendar_get_valid_event_timestart_range_open_event(): void {
1 efrain 982
        global $CFG;
983
        require_once($CFG->dirroot . "/calendar/lib.php");
984
 
985
        $this->resetAfterTest(true);
986
        $this->setAdminUser();
987
        $generator = $this->getDataGenerator();
988
        $course = $generator->create_course();
989
        $timeopen = time();
990
        $timeclose = $timeopen + DAYSECS;
991
        $lesson = new \stdClass();
992
        $lesson->available = $timeopen;
993
        $lesson->deadline = $timeclose;
994
 
995
        // Create a valid event.
996
        $event = new \calendar_event([
997
            'name' => 'Test event',
998
            'description' => '',
999
            'format' => 1,
1000
            'courseid' => $course->id,
1001
            'groupid' => 0,
1002
            'userid' => 2,
1003
            'modulename' => 'lesson',
1004
            'instance' => 1,
1005
            'eventtype' => LESSON_EVENT_TYPE_OPEN,
1006
            'timestart' => 1,
1007
            'timeduration' => 86400,
1008
            'visible' => 1
1009
        ]);
1010
 
1011
        // The max limit should be bounded by the timeclose value.
1012
        list ($min, $max) = mod_lesson_core_calendar_get_valid_event_timestart_range($event, $lesson);
1013
        $this->assertNull($min);
1014
        $this->assertEquals($timeclose, $max[0]);
1015
 
1016
        // No timeclose value should result in no upper limit.
1017
        $lesson->deadline = 0;
1018
        list ($min, $max) = mod_lesson_core_calendar_get_valid_event_timestart_range($event, $lesson);
1019
        $this->assertNull($min);
1020
        $this->assertNull($max);
1021
    }
1022
 
1023
    /**
1024
     * The close event should be limited by the lesson's available property, if it's set.
1025
     */
11 efrain 1026
    public function test_mod_lesson_core_calendar_get_valid_event_timestart_range_close_event(): void {
1 efrain 1027
        global $CFG;
1028
        require_once($CFG->dirroot . "/calendar/lib.php");
1029
 
1030
        $this->resetAfterTest(true);
1031
        $this->setAdminUser();
1032
        $generator = $this->getDataGenerator();
1033
        $course = $generator->create_course();
1034
        $timeopen = time();
1035
        $timeclose = $timeopen + DAYSECS;
1036
        $lesson = new \stdClass();
1037
        $lesson->available = $timeopen;
1038
        $lesson->deadline = $timeclose;
1039
 
1040
        // Create a valid event.
1041
        $event = new \calendar_event([
1042
            'name' => 'Test event',
1043
            'description' => '',
1044
            'format' => 1,
1045
            'courseid' => $course->id,
1046
            'groupid' => 0,
1047
            'userid' => 2,
1048
            'modulename' => 'lesson',
1049
            'instance' => 1,
1050
            'eventtype' => LESSON_EVENT_TYPE_CLOSE,
1051
            'timestart' => 1,
1052
            'timeduration' => 86400,
1053
            'visible' => 1
1054
        ]);
1055
 
1056
        // The max limit should be bounded by the timeclose value.
1057
        list ($min, $max) = mod_lesson_core_calendar_get_valid_event_timestart_range($event, $lesson);
1058
        $this->assertEquals($timeopen, $min[0]);
1059
        $this->assertNull($max);
1060
 
1061
        // No deadline value should result in no upper limit.
1062
        $lesson->available = 0;
1063
        list ($min, $max) = mod_lesson_core_calendar_get_valid_event_timestart_range($event, $lesson);
1064
        $this->assertNull($min);
1065
        $this->assertNull($max);
1066
    }
1067
 
1068
    /**
1069
     * A user who does not have capabilities to add events to the calendar should be able to create an lesson.
1070
     */
11 efrain 1071
    public function test_creation_with_no_calendar_capabilities(): void {
1 efrain 1072
        $this->resetAfterTest();
1073
        $course = self::getDataGenerator()->create_course();
1074
        $context = \context_course::instance($course->id);
1075
        $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher');
1076
        $roleid = self::getDataGenerator()->create_role();
1077
        self::getDataGenerator()->role_assign($roleid, $user->id, $context->id);
1078
        assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true);
1079
        $generator = self::getDataGenerator()->get_plugin_generator('mod_lesson');
1080
        // Create an instance as a user without the calendar capabilities.
1081
        $this->setUser($user);
1082
        $time = time();
1083
        $params = array(
1084
            'course' => $course->id,
1085
            'available' => $time + 200,
1086
            'deadline' => $time + 2000,
1087
        );
1088
        $generator->create_instance($params);
1089
    }
1090
}