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
 * Course related unit tests
19
 *
20
 * @package    core_course
21
 * @copyright  2014 Marina Glancy
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 * @covers     \core_courseformat\base
24
 * @coversDefaultClass \core_courseformat\base
25
 */
26
class base_test extends advanced_testcase {
27
 
28
    /**
29
     * Setup to ensure that fixtures are loaded.
30
     */
31
    public static function setupBeforeClass(): void {
32
        global $CFG;
33
        require_once($CFG->dirroot . '/course/lib.php');
34
        require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest.php');
35
        require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest_output_course_format_state.php');
36
        require_once($CFG->dirroot . '/course/format/tests/fixtures/format_theunittest_output_course_format_invalidoutput.php');
37
    }
38
 
39
    /**
40
     * Tests the save and load functionality.
41
     *
42
     * @author Jason den Dulk
43
     * @covers \core_courseformat
44
     */
11 efrain 45
    public function test_courseformat_saveandload(): void {
1 efrain 46
        $this->resetAfterTest();
47
 
48
        $courseformatoptiondata = (object) [
49
            "hideoddsections" => 1,
50
            'summary_editor' => [
51
                'text' => '<p>Somewhere over the rainbow</p><p>The <b>quick</b> brown fox jumpos over the lazy dog.</p>',
52
                'format' => 1
53
            ]
54
        ];
55
        $generator = $this->getDataGenerator();
56
        $course1 = $generator->create_course(array('format' => 'theunittest'));
57
        $this->assertEquals('theunittest', $course1->format);
58
        course_create_sections_if_missing($course1, array(0, 1));
59
 
60
        $courseformat = course_get_format($course1);
61
        $courseformat->update_course_format_options($courseformatoptiondata);
62
 
63
        $savedcourseformatoptiondata = $courseformat->get_format_options();
64
 
65
        $this->assertEqualsCanonicalizing($courseformatoptiondata, (object) $savedcourseformatoptiondata);
66
    }
67
 
11 efrain 68
    public function test_available_hook(): void {
1 efrain 69
        global $DB;
70
        $this->resetAfterTest();
71
 
72
        // Generate a course with two sections (0 and 1) and two modules. Course format is set to 'theunittest'.
73
        $generator = $this->getDataGenerator();
74
        $course1 = $generator->create_course(array('format' => 'theunittest'));
75
        $this->assertEquals('theunittest', $course1->format);
76
        course_create_sections_if_missing($course1, array(0, 1));
77
        $assign0 = $generator->create_module('assign', array('course' => $course1, 'section' => 0));
78
        $assign1 = $generator->create_module('assign', array('course' => $course1, 'section' => 1));
79
        $assign2 = $generator->create_module('assign', array('course' => $course1, 'section' => 0, 'visible' => 0));
80
 
81
        // Create a courseoverview role based on the student role.
82
        $roleattr = array('name' => 'courseoverview', 'shortname' => 'courseoverview', 'archetype' => 'student');
83
        $generator->create_role($roleattr);
84
 
85
        // Create user student, editingteacher, teacher and courseoverview.
86
        $student = $generator->create_user();
87
        $teacher = $generator->create_user();
88
        $editingteacher = $generator->create_user();
89
        $courseoverviewuser = $generator->create_user();
90
 
91
        // Enrol users into their roles.
92
        $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
93
        $generator->enrol_user($student->id, $course1->id, $roleids['student']);
94
        $generator->enrol_user($teacher->id, $course1->id, $roleids['teacher']);
95
        $generator->enrol_user($editingteacher->id, $course1->id, $roleids['editingteacher']);
96
        $generator->enrol_user($courseoverviewuser->id, $course1->id, $roleids['courseoverview']);
97
 
98
        // Remove the ignoreavailabilityrestrictions from the teacher role.
99
        role_change_permission($roleids['teacher'], context_system::instance(0),
100
                'moodle/course:ignoreavailabilityrestrictions', CAP_PREVENT);
101
 
102
        // Allow the courseoverview role to ingore available restriction.
103
        role_change_permission($roleids['courseoverview'], context_system::instance(0),
104
                'moodle/course:ignoreavailabilityrestrictions', CAP_ALLOW);
105
 
106
        // Make sure that initially both sections and both modules are available and visible for a student.
107
        $modinfostudent = get_fast_modinfo($course1, $student->id);
108
        $this->assertTrue($modinfostudent->get_section_info(1)->available);
109
        $this->assertTrue($modinfostudent->get_cm($assign0->cmid)->available);
110
        $this->assertTrue($modinfostudent->get_cm($assign0->cmid)->uservisible);
111
        $this->assertTrue($modinfostudent->get_cm($assign1->cmid)->available);
112
        $this->assertTrue($modinfostudent->get_cm($assign1->cmid)->uservisible);
113
        $this->assertFalse($modinfostudent->get_cm($assign2->cmid)->uservisible);
114
 
115
        // Set 'hideoddsections' for the course to 1.
116
        // Section1 and assign1 will be unavailable, uservisible will be false for student and true for teacher.
117
        $data = (object)array('id' => $course1->id, 'hideoddsections' => 1);
118
        course_get_format($course1)->update_course_format_options($data);
119
        $modinfostudent = get_fast_modinfo($course1, $student->id);
120
        $this->assertFalse($modinfostudent->get_section_info(1)->available);
121
        $this->assertEmpty($modinfostudent->get_section_info(1)->availableinfo);
122
        $this->assertFalse($modinfostudent->get_section_info(1)->uservisible);
123
        $this->assertTrue($modinfostudent->get_cm($assign0->cmid)->available);
124
        $this->assertTrue($modinfostudent->get_cm($assign0->cmid)->uservisible);
125
        $this->assertFalse($modinfostudent->get_cm($assign1->cmid)->available);
126
        $this->assertFalse($modinfostudent->get_cm($assign1->cmid)->uservisible);
127
        $this->assertFalse($modinfostudent->get_cm($assign2->cmid)->uservisible);
128
 
129
        $modinfoteacher = get_fast_modinfo($course1, $teacher->id);
130
        $this->assertFalse($modinfoteacher->get_section_info(1)->available);
131
        $this->assertEmpty($modinfoteacher->get_section_info(1)->availableinfo);
132
        $this->assertFalse($modinfoteacher->get_section_info(1)->uservisible);
133
        $this->assertTrue($modinfoteacher->get_cm($assign0->cmid)->available);
134
        $this->assertTrue($modinfoteacher->get_cm($assign0->cmid)->uservisible);
135
        $this->assertFalse($modinfoteacher->get_cm($assign1->cmid)->available);
136
        $this->assertFalse($modinfoteacher->get_cm($assign1->cmid)->uservisible);
137
        $this->assertTrue($modinfoteacher->get_cm($assign2->cmid)->available);
138
        $this->assertTrue($modinfoteacher->get_cm($assign2->cmid)->uservisible);
139
 
140
        $modinfoteacher = get_fast_modinfo($course1, $editingteacher->id);
141
        $this->assertFalse($modinfoteacher->get_section_info(1)->available);
142
        $this->assertEmpty($modinfoteacher->get_section_info(1)->availableinfo);
143
        $this->assertTrue($modinfoteacher->get_section_info(1)->uservisible);
144
        $this->assertTrue($modinfoteacher->get_cm($assign0->cmid)->available);
145
        $this->assertTrue($modinfoteacher->get_cm($assign0->cmid)->uservisible);
146
        $this->assertFalse($modinfoteacher->get_cm($assign1->cmid)->available);
147
        $this->assertTrue($modinfoteacher->get_cm($assign1->cmid)->uservisible);
148
        $this->assertTrue($modinfoteacher->get_cm($assign2->cmid)->uservisible);
149
 
150
        $modinfocourseoverview = get_fast_modinfo($course1, $courseoverviewuser->id);
151
        $this->assertFalse($modinfocourseoverview->get_section_info(1)->available);
152
        $this->assertEmpty($modinfocourseoverview->get_section_info(1)->availableinfo);
153
        $this->assertTrue($modinfocourseoverview->get_section_info(1)->uservisible);
154
        $this->assertTrue($modinfocourseoverview->get_cm($assign0->cmid)->available);
155
        $this->assertTrue($modinfocourseoverview->get_cm($assign0->cmid)->uservisible);
156
        $this->assertFalse($modinfocourseoverview->get_cm($assign1->cmid)->available);
157
        $this->assertTrue($modinfocourseoverview->get_cm($assign1->cmid)->uservisible);
158
        $this->assertFalse($modinfocourseoverview->get_cm($assign2->cmid)->uservisible);
159
 
160
        // Set 'hideoddsections' for the course to 2.
161
        // Section1 and assign1 will be unavailable, uservisible will be false for student and true for teacher.
162
        // Property availableinfo will be not empty.
163
        $data = (object)array('id' => $course1->id, 'hideoddsections' => 2);
164
        course_get_format($course1)->update_course_format_options($data);
165
        $modinfostudent = get_fast_modinfo($course1, $student->id);
166
        $this->assertFalse($modinfostudent->get_section_info(1)->available);
167
        $this->assertNotEmpty($modinfostudent->get_section_info(1)->availableinfo);
168
        $this->assertFalse($modinfostudent->get_section_info(1)->uservisible);
169
        $this->assertTrue($modinfostudent->get_cm($assign0->cmid)->available);
170
        $this->assertTrue($modinfostudent->get_cm($assign0->cmid)->uservisible);
171
        $this->assertFalse($modinfostudent->get_cm($assign1->cmid)->available);
172
        $this->assertFalse($modinfostudent->get_cm($assign1->cmid)->uservisible);
173
 
174
        $modinfoteacher = get_fast_modinfo($course1, $editingteacher->id);
175
        $this->assertFalse($modinfoteacher->get_section_info(1)->available);
176
        $this->assertNotEmpty($modinfoteacher->get_section_info(1)->availableinfo);
177
        $this->assertTrue($modinfoteacher->get_section_info(1)->uservisible);
178
        $this->assertTrue($modinfoteacher->get_cm($assign0->cmid)->available);
179
        $this->assertTrue($modinfoteacher->get_cm($assign0->cmid)->uservisible);
180
        $this->assertFalse($modinfoteacher->get_cm($assign1->cmid)->available);
181
        $this->assertTrue($modinfoteacher->get_cm($assign1->cmid)->uservisible);
182
    }
183
 
184
    /**
185
     * Test for supports_news() with a course format plugin that doesn't define 'news_items' in default blocks.
186
     */
11 efrain 187
    public function test_supports_news(): void {
1 efrain 188
        $this->resetAfterTest();
189
        $format = course_get_format((object)['format' => 'testformat']);
190
        $this->assertFalse($format->supports_news());
191
    }
192
 
193
    /**
194
     * Test for supports_news() for old course format plugins that defines 'news_items' in default blocks.
195
     */
11 efrain 196
    public function test_supports_news_legacy(): void {
1 efrain 197
        $this->resetAfterTest();
198
        $format = course_get_format((object)['format' => 'testlegacy']);
199
        $this->assertTrue($format->supports_news());
200
    }
201
 
202
    /**
203
     * Test for get_view_url().
204
     *
205
     * @covers ::get_view_url
206
     */
207
    public function test_get_view_url(): void {
208
        global $CFG;
209
        $this->resetAfterTest();
210
 
211
        // Generate a course with two sections (0 and 1) and two modules. Course format is set to 'testformat'.
212
        // This will allow us to test the default implementation of get_view_url.
213
        $generator = $this->getDataGenerator();
214
        $course1 = $generator->create_course(array('format' => 'testformat'));
215
        course_create_sections_if_missing($course1, array(0, 1));
216
 
217
        $data = (object)['id' => $course1->id];
218
        $format = course_get_format($course1);
219
        $format->update_course_format_options($data);
220
 
221
        // In page.
222
        $this->assertNotEmpty($format->get_view_url(null));
223
        $this->assertNotEmpty($format->get_view_url(0));
224
        $this->assertNotEmpty($format->get_view_url(1));
225
 
226
        // Navigation.
227
        $this->assertStringContainsString('course/view.php', $format->get_view_url(0));
228
        $this->assertStringContainsString('course/view.php', $format->get_view_url(1));
229
        $this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['navigation' => 1]));
230
        $this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['navigation' => 1]));
231
        // When sr parameter is defined, the section.php page should be returned.
232
        $this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['sr' => 1]));
233
        $this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['sr' => 1]));
234
        $this->assertStringContainsString('course/section.php', $format->get_view_url(0, ['sr' => 0]));
235
        $this->assertStringContainsString('course/section.php', $format->get_view_url(1, ['sr' => 0]));
236
 
237
        // Expand section.
238
        // The current course format $format uses the format 'testformat' which does not use sections.
239
        // Thus, the 'expanded' parameter does not do anything.
240
        $viewurl = $format->get_view_url(1);
241
        $this->assertNull($viewurl->get_param('expandsection'));
242
        $viewurl = $format->get_view_url(1, ['expanded' => 1]);
243
        $this->assertNull($viewurl->get_param('expandsection'));
244
        $viewurl = $format->get_view_url(1, ['expanded' => 0]);
245
        $this->assertNull($viewurl->get_param('expandsection'));
246
        // We now use a course format which uses sections.
247
        $course2 = $generator->create_course(['format' => 'testformatsections']);
248
        course_create_sections_if_missing($course1, [0, 2]);
249
        $formatwithsections = course_get_format($course2);
250
        $viewurl = $formatwithsections->get_view_url(2);
251
        $this->assertEquals(2, $viewurl->get_param('expandsection'));
252
        $viewurl = $formatwithsections->get_view_url(2, ['expanded' => 1]);
253
        $this->assertEquals(2, $viewurl->get_param('expandsection'));
254
        $viewurl = $formatwithsections->get_view_url(2, ['expanded' => 0]);
255
        $this->assertNull($viewurl->get_param('expandsection'));
256
    }
257
 
258
    /**
259
     * Test for get_output_classname method.
260
     *
261
     * @dataProvider get_output_classname_provider
262
     * @param string $find the class to find
263
     * @param string $result the expected result classname
264
     * @param bool $exception if the method will raise an exception
265
     */
11 efrain 266
    public function test_get_output_classname($find, $result, $exception): void {
1 efrain 267
        $this->resetAfterTest();
268
 
269
        $course = $this->getDataGenerator()->create_course(['format' => 'theunittest']);
270
        $courseformat = course_get_format($course);
271
 
272
        if ($exception) {
273
            $this->expectException(coding_exception::class);
274
        }
275
 
276
        $courseclass = $courseformat->get_output_classname($find);
277
        $this->assertEquals($result, $courseclass);
278
    }
279
 
280
    /**
281
     * Data provider for test_get_output_classname.
282
     *
283
     * @return array the testing scenarios
284
     */
285
    public function get_output_classname_provider(): array {
286
        return [
287
            'overridden class' => [
288
                'find' => 'state\\course',
289
                'result' => 'format_theunittest\\output\\courseformat\\state\\course',
290
                'exception' => false,
291
            ],
292
            'original class' => [
293
                'find' => 'state\\section',
294
                'result' => 'core_courseformat\\output\\local\\state\\section',
295
                'exception' => false,
296
            ],
297
            'invalid overridden class' => [
298
                'find' => 'state\\invalidoutput',
299
                'result' => '',
300
                'exception' => true,
301
            ],
302
        ];
303
    }
304
 
305
    /**
306
     * Test for the default delete format data behaviour.
307
     *
308
     * @covers ::get_sections_preferences
309
     */
11 efrain 310
    public function test_get_sections_preferences(): void {
1 efrain 311
        $this->resetAfterTest();
312
        $generator = $this->getDataGenerator();
313
        $course = $generator->create_course();
314
        $user = $generator->create_and_enrol($course, 'student');
315
 
316
        // Create fake preferences generated by the frontend js module.
317
        $data = (object)[
318
            'pref1' => [1,2],
319
            'pref2' => [1],
320
        ];
321
        set_user_preference('coursesectionspreferences_' . $course->id, json_encode($data), $user->id);
322
 
323
        $format = course_get_format($course);
324
 
325
        // Load data from user 1.
326
        $this->setUser($user);
327
        $preferences = $format->get_sections_preferences();
328
 
329
        $this->assertEquals(
330
            (object)['pref1' => true, 'pref2' => true],
331
            $preferences[1]
332
        );
333
        $this->assertEquals(
334
            (object)['pref1' => true],
335
            $preferences[2]
336
        );
337
    }
338
 
339
    /**
340
     * Test for the default delete format data behaviour.
341
     *
342
     * @covers ::set_sections_preference
343
     */
11 efrain 344
    public function test_set_sections_preference(): void {
1 efrain 345
        $this->resetAfterTest();
346
        $generator = $this->getDataGenerator();
347
        $course = $generator->create_course();
348
        $user = $generator->create_and_enrol($course, 'student');
349
 
350
        $format = course_get_format($course);
351
        $this->setUser($user);
352
 
353
        // Load data from user 1.
354
        $format->set_sections_preference('pref1', [1, 2]);
355
        $format->set_sections_preference('pref2', [1]);
356
        $format->set_sections_preference('pref3', []);
357
 
358
        $preferences = $format->get_sections_preferences();
359
        $this->assertEquals(
360
            (object)['pref1' => true, 'pref2' => true],
361
            $preferences[1]
362
        );
363
        $this->assertEquals(
364
            (object)['pref1' => true],
365
            $preferences[2]
366
        );
367
    }
368
 
369
    /**
370
     * Test add_section_preference_ids() method.
371
     *
372
     * @covers \core_courseformat\base::persist_to_user_preference
373
     */
374
    public function test_add_section_preference_ids(): void {
375
        $this->resetAfterTest();
376
        // Create initial data.
377
        $generator = $this->getDataGenerator();
378
        $course = $generator->create_course();
379
        $user = $generator->create_and_enrol($course);
380
        // Get the course format.
381
        $format = course_get_format($course);
382
        // Login as the user.
383
        $this->setUser($user);
384
 
385
        // Add section preference ids.
386
        $format->add_section_preference_ids('pref1', [1, 2]);
387
        $format->add_section_preference_ids('pref1', [3]);
388
        $format->add_section_preference_ids('pref2', [1]);
389
 
390
        // Get section preferences.
391
        $sectionpreferences = $format->get_sections_preferences_by_preference();
392
        $this->assertCount(3, $sectionpreferences['pref1']);
393
        $this->assertContains(1, $sectionpreferences['pref1']);
394
        $this->assertContains(2, $sectionpreferences['pref1']);
395
        $this->assertContains(3, $sectionpreferences['pref1']);
396
        $this->assertCount(1, $sectionpreferences['pref2']);
397
        $this->assertContains(1, $sectionpreferences['pref1']);
398
    }
399
 
400
    /**
401
     * Test remove_section_preference_ids() method.
402
     *
403
     * @covers \core_courseformat\base::persist_to_user_preference
404
     */
405
    public function test_remove_section_preference_ids(): void {
406
        $this->resetAfterTest();
407
        // Create initial data.
408
        $generator = $this->getDataGenerator();
409
        $course = $generator->create_course();
410
        $user = $generator->create_and_enrol($course);
411
        // Get the course format.
412
        $format = course_get_format($course);
413
        // Login as the user.
414
        $this->setUser($user);
415
        // Set initial preferences.
416
        $format->set_sections_preference('pref1', [1, 2, 3]);
417
        $format->set_sections_preference('pref2', [1]);
418
 
419
        // Remove section with id = 3 out of the pref1.
420
        $format->remove_section_preference_ids('pref1', [3]);
421
        // Get section preferences.
422
        $sectionpreferences = $format->get_sections_preferences_by_preference();
423
        $this->assertCount(2, $sectionpreferences['pref1']);
424
        $this->assertCount(1, $sectionpreferences['pref2']);
425
 
426
        // Remove section with id = 2 out of the pref1.
427
        $format->remove_section_preference_ids('pref1', [2]);
428
        // Remove section with id = 1 out of the pref2.
429
        $format->remove_section_preference_ids('pref2', [1]);
430
        // Get section preferences.
431
        $sectionpreferences = $format->get_sections_preferences_by_preference();
432
        $this->assertCount(1, $sectionpreferences['pref1']);
433
        $this->assertEmpty($sectionpreferences['pref2']);
434
    }
435
 
436
    /**
437
     * Test that retrieving last section number for a course
438
     *
439
     * @covers ::get_last_section_number
440
     */
441
    public function test_get_last_section_number(): void {
442
        global $DB;
443
 
444
        $this->resetAfterTest();
445
 
446
        // Course with two additional sections.
447
        $courseone = $this->getDataGenerator()->create_course(['numsections' => 2]);
448
        $this->assertEquals(2, course_get_format($courseone)->get_last_section_number());
449
 
450
        // Course without additional sections, section zero is the "default" section that always exists.
451
        $coursetwo = $this->getDataGenerator()->create_course(['numsections' => 0]);
452
        $this->assertEquals(0, course_get_format($coursetwo)->get_last_section_number());
453
 
454
        // Course without additional sections, manually remove section zero, as "course_delete_section" prevents that. This
455
        // simulates course data integrity issues that previously triggered errors.
456
        $coursethree = $this->getDataGenerator()->create_course(['numsections' => 0]);
457
        $DB->delete_records('course_sections', ['course' => $coursethree->id, 'section' => 0]);
458
 
459
        $this->assertEquals(-1, course_get_format($coursethree)->get_last_section_number());
460
    }
461
 
462
    /**
463
     * Test for the default delete format data behaviour.
464
     *
465
     * @covers ::delete_format_data
466
     * @dataProvider delete_format_data_provider
467
     * @param bool $usehook if it should use course_delete to trigger $format->delete_format_data as a hook
468
     */
11 efrain 469
    public function test_delete_format_data(bool $usehook): void {
1 efrain 470
        global $DB;
471
 
472
        $this->resetAfterTest();
473
 
474
        $generator = $this->getDataGenerator();
475
        $course = $generator->create_course();
476
        course_create_sections_if_missing($course, [0, 1]);
477
        $user = $generator->create_and_enrol($course, 'student');
478
 
479
        // Create a coursesectionspreferences_XX preference.
480
        $key = 'coursesectionspreferences_' . $course->id;
481
        $fakevalue = 'No dark sarcasm in the classroom';
482
        set_user_preference($key, $fakevalue, $user->id);
483
        $this->assertEquals(
484
            $fakevalue,
485
            $DB->get_field('user_preferences', 'value', ['name' => $key, 'userid' => $user->id])
486
        );
487
 
488
        // Create another random user preference.
489
        $key2 = 'somepreference';
490
        $fakevalue2 = "All in all it's just another brick in the wall";
491
        set_user_preference($key2, $fakevalue2, $user->id);
492
        $this->assertEquals(
493
            $fakevalue2,
494
            $DB->get_field('user_preferences', 'value', ['name' => $key2, 'userid' => $user->id])
495
        );
496
 
497
        if ($usehook) {
498
            delete_course($course, false);
499
        } else {
500
            $format = course_get_format($course);
501
            $format->delete_format_data();
502
        }
503
 
504
        // Check which the preferences exists.
505
        $this->assertFalse(
506
            $DB->record_exists('user_preferences', ['name' => $key, 'userid' => $user->id])
507
        );
508
        set_user_preference($key2, $fakevalue2, $user->id);
509
        $this->assertEquals(
510
            $fakevalue2,
511
            $DB->get_field('user_preferences', 'value', ['name' => $key2, 'userid' => $user->id])
512
        );
513
    }
514
 
515
    /**
516
     * Data provider for test_delete_format_data.
517
     *
518
     * @return array the testing scenarios
519
     */
520
    public function delete_format_data_provider(): array {
521
        return [
522
            'direct call' => [
523
                'usehook' => false
524
            ],
525
            'use hook' => [
526
                'usehook' => true,
527
            ]
528
        ];
529
    }
530
 
531
    /**
532
     * Test duplicate_section()
533
     * @covers ::duplicate_section
534
     */
11 efrain 535
    public function test_duplicate_section(): void {
1 efrain 536
        global $DB;
537
 
538
        $this->setAdminUser();
539
        $this->resetAfterTest();
540
 
541
        $generator = $this->getDataGenerator();
542
        $course = $generator->create_course();
543
        $format = course_get_format($course);
544
 
545
        $originalsection = $DB->get_record('course_sections', ['course' => $course->id, 'section' => 1], '*', MUST_EXIST);
546
        $generator->create_module('page', ['course' => $course, 'section' => $originalsection->section]);
547
        $generator->create_module('page', ['course' => $course, 'section' => $originalsection->section]);
548
        $generator->create_module('page', ['course' => $course, 'section' => $originalsection->section]);
549
 
550
        $originalmodcount = $DB->count_records('course_modules', ['course' => $course->id, 'section' => $originalsection->id]);
551
        $this->assertEquals(3, $originalmodcount);
552
 
553
        $modinfo = get_fast_modinfo($course);
554
        $sectioninfo = $modinfo->get_section_info($originalsection->section, MUST_EXIST);
555
 
556
        $newsection = $format->duplicate_section($sectioninfo);
557
 
558
        // Verify properties are the same.
559
        foreach ($originalsection as $prop => $value) {
560
            if ($prop == 'id' || $prop == 'sequence' || $prop == 'section' || $prop == 'timemodified') {
561
                continue;
562
            }
563
            $this->assertEquals($value, $newsection->$prop);
564
        }
565
 
566
        $newmodcount = $DB->count_records('course_modules', ['course' => $course->id, 'section' => $newsection->id]);
567
        $this->assertEquals($originalmodcount, $newmodcount);
568
    }
569
 
570
    /**
571
     * Test for the default delete format data behaviour.
572
     *
573
     * @covers ::get_format_string
574
     * @dataProvider get_format_string_provider
575
     * @param string $key the string key
576
     * @param string|null $data any string data
577
     * @param array|null $expectedstring the expected string (null for exception)
578
     * @param string $courseformat the course format
579
     */
580
    public function test_get_format_string(
581
        string $key,
582
        ?string $data,
583
        ?array $expectedstring,
584
        string $courseformat = 'topics'
585
    ): void {
586
        global $DB;
587
 
588
        $this->resetAfterTest();
589
 
590
        $generator = $this->getDataGenerator();
591
        $course = $generator->create_course(['format' => $courseformat]);
592
 
593
        if ($expectedstring) {
594
            $expected = get_string($expectedstring[0], $expectedstring[1], $expectedstring[2]);
595
        } else {
596
            $this->expectException(\coding_exception::class);
597
        }
598
        $format = course_get_format($course);
599
        $result = $format->get_format_string($key, $data);
600
        $this->assertEquals($expected, $result);
601
    }
602
 
603
    /**
604
     * Data provider for test_get_format_string.
605
     *
606
     * @return array the testing scenarios
607
     */
608
    public function get_format_string_provider(): array {
609
        return [
610
            'Existing in format lang' => [
611
                'key' => 'addsection',
612
                'data' => null,
613
                'expectedstring' => ['addsection', 'format_weeks', null],
614
                'courseformat' => 'weeks',
615
            ],
616
            'Not existing in format lang' => [
617
                'key' => 'bulkedit',
618
                'data' => null,
619
                'expectedstring' => ['bulkedit', 'core_courseformat', null],
620
            ],
621
            'Existing in format lang with data' => [
622
                'key' => 'section_highlight_feedback',
623
                'data' => 'Example',
624
                'expectedstring' => ['section_highlight_feedback', 'format_topics', 'Example'],
625
            ],
626
            'Not existing in format lang with data' => [
627
                'key' => 'bulkselection',
628
                'data' => 'X',
629
                'expectedstring' => ['bulkselection', 'core_courseformat', 'X'],
630
            ],
631
            'Non existing string' => [
632
                'key' => '%&non_existing_string_in_lang_files$%@#',
633
                'data' => null,
634
                'expectedstring' => null,
635
            ],
636
        ];
637
    }
638
 
639
    /**
640
     * Test for the move_section_after method.
641
     *
642
     * @covers ::move_section_after
643
     * @dataProvider move_section_after_provider
644
     * @param string $movesection the reference of the section to move
645
     * @param string $destination the reference of the destination section
646
     * @param string[] $order the references of the final section order
647
     */
11 efrain 648
    public function test_move_section_after(string $movesection, string $destination, array $order): void {
1 efrain 649
        global $DB;
650
 
651
        $this->resetAfterTest();
652
 
653
        $generator = $this->getDataGenerator();
654
        $course = $generator->create_course();
655
        course_create_sections_if_missing($course, [0, 1, 2, 3, 4, 5]);
656
 
657
        $format = course_get_format($course);
658
        $modinfo = $format->get_modinfo();
659
        $sectionsinfo = $modinfo->get_section_info_all();
660
 
661
        $references = [];
662
        foreach ($sectionsinfo as $section) {
663
            $references["section{$section->section}"] = $section;
664
        }
665
 
666
        $result = $format->move_section_after(
667
            $references[$movesection],
668
            $references[$destination]
669
        );
670
        $this->assertEquals(true, $result);
671
        // Check the updated course section list.
672
        $modinfo = $format->get_modinfo();
673
        $sectionsinfo = $modinfo->get_section_info_all();
674
        $this->assertCount(count($order), $sectionsinfo);
675
        foreach ($sectionsinfo as $key => $section) {
676
            $sectionreference = $order[$key];
677
            $oldinfo = $references[$sectionreference];
678
            $this->assertEquals($oldinfo->id, $section->id);
679
        }
680
    }
681
 
682
    /**
683
     * Data provider for test_move_section_after.
684
     *
685
     * @return array the testing scenarios
686
     */
687
    public function move_section_after_provider(): array {
688
        return [
689
            'Move top' => [
690
                'movesection' => 'section3',
691
                'destination' => 'section0',
692
                'order' => [
693
                    'section0',
694
                    'section3',
695
                    'section1',
696
                    'section2',
697
                    'section4',
698
                    'section5',
699
                ],
700
            ],
701
            'Move up' => [
702
                'movesection' => 'section3',
703
                'destination' => 'section1',
704
                'order' => [
705
                    'section0',
706
                    'section1',
707
                    'section3',
708
                    'section2',
709
                    'section4',
710
                    'section5',
711
                ],
712
            ],
713
            'Do not move' => [
714
                'movesection' => 'section3',
715
                'destination' => 'section2',
716
                'order' => [
717
                    'section0',
718
                    'section1',
719
                    'section2',
720
                    'section3',
721
                    'section4',
722
                    'section5',
723
                ],
724
            ],
725
            'Same position' => [
726
                'movesection' => 'section3',
727
                'destination' => 'section3',
728
                'order' => [
729
                    'section0',
730
                    'section1',
731
                    'section2',
732
                    'section3',
733
                    'section4',
734
                    'section5',
735
                ],
736
            ],
737
            'Move down' => [
738
                'movesection' => 'section3',
739
                'destination' => 'section4',
740
                'order' => [
741
                    'section0',
742
                    'section1',
743
                    'section2',
744
                    'section4',
745
                    'section3',
746
                    'section5',
747
                ],
748
            ],
749
            'Move bottom' => [
750
                'movesection' => 'section3',
751
                'destination' => 'section5',
752
                'order' => [
753
                    'section0',
754
                    'section1',
755
                    'section2',
756
                    'section4',
757
                    'section5',
758
                    'section3',
759
                ],
760
            ],
761
        ];
762
    }
763
 
764
    /**
765
     * Test for the get_non_ajax_cm_action_url method.
766
     *
767
     * @covers ::get_non_ajax_cm_action_url
768
     * @dataProvider get_non_ajax_cm_action_url_provider
769
     * @param string $action the ajax action name
770
     * @param string $expectedparam the expected param to check
771
     * @param string $exception if an exception is expected
772
     */
11 efrain 773
    public function test_get_non_ajax_cm_action_url(string $action, string $expectedparam, bool $exception): void {
1 efrain 774
        global $DB;
775
 
776
        $this->resetAfterTest();
777
 
778
        $generator = $this->getDataGenerator();
779
        $course = $generator->create_course();
780
        $assign0 = $generator->create_module('assign', array('course' => $course, 'section' => 0));
781
 
782
        $format = course_get_format($course);
783
        $modinfo = $format->get_modinfo();
784
        $cminfo = $modinfo->get_cm($assign0->cmid);
785
 
786
        if ($exception) {
787
            $this->expectException(\coding_exception::class);
788
        }
789
        $result = $format->get_non_ajax_cm_action_url($action, $cminfo);
790
        $this->assertEquals($assign0->cmid, $result->param($expectedparam));
791
    }
792
 
793
    /**
794
     * Data provider for test_get_non_ajax_cm_action_url.
795
     *
796
     * @return array the testing scenarios
797
     */
798
    public function get_non_ajax_cm_action_url_provider(): array {
799
        return [
800
            'duplicate' => [
801
                'action' => 'cmDuplicate',
802
                'expectedparam' => 'duplicate',
803
                'exception' => false,
804
            ],
805
            'hide' => [
806
                'action' => 'cmHide',
807
                'expectedparam' => 'hide',
808
                'exception' => false,
809
            ],
810
            'show' => [
811
                'action' => 'cmShow',
812
                'expectedparam' => 'show',
813
                'exception' => false,
814
            ],
815
            'stealth' => [
816
                'action' => 'cmStealth',
817
                'expectedparam' => 'stealth',
818
                'exception' => false,
819
            ],
820
            'delete' => [
821
                'action' => 'cmDelete',
822
                'expectedparam' => 'delete',
823
                'exception' => false,
824
            ],
825
            'non-existent' => [
826
                'action' => 'nonExistent',
827
                'expectedparam' => '',
828
                'exception' => true,
829
            ],
830
        ];
831
    }
832
 
833
    /**
834
     * Test get_required_jsfiles().
835
     *
836
     * @covers ::get_required_jsfiles
837
     */
838
    public function test_get_required_jsfiles(): void {
839
        $this->resetAfterTest();
840
 
841
        $generator = $this->getDataGenerator();
842
 
843
        $course = $generator->create_course(['format' => 'testformat']);
844
        $format = course_get_format($course);
845
        $this->assertEmpty($format->get_required_jsfiles());
846
    }
847
 
848
    /**
849
     * Test set_sectionid().
850
     *
851
     * @covers ::set_sectionid
852
     * @covers ::get_sectionid
853
     * @covers ::get_sectionnum
854
     */
855
    public function test_set_sectionid(): void {
856
        $this->resetAfterTest();
857
 
858
        $generator = $this->getDataGenerator();
859
        $course = $generator->create_course(['numsections' => 2]);
860
        $format = course_get_format($course);
861
 
862
        // No section.
863
        $this->assertNull($format->get_sectionid());
864
        $this->assertNull($format->get_sectionnum());
865
 
866
        // Valid section.
867
        $sectionnum = 1;
868
        $modinfo = get_fast_modinfo($course);
869
        $sectioninfo = $modinfo->get_section_info($sectionnum);
870
        $sectionid = $sectioninfo->id;
871
        $format->set_sectionid($sectionid);
872
        $this->assertEquals($sectionid, $format->get_sectionid());
873
        $this->assertEquals($sectionnum, $format->get_sectionnum());
874
 
875
        // Null section.
876
        $format->set_sectionid(null);
877
        $this->assertNull($format->get_sectionid());
878
        $this->assertNull($format->get_sectionnum());
879
 
880
        // Invalid section.
881
        $this->expectException(\coding_exception::class);
882
        $format->set_sectionid(-1);
883
    }
884
 
885
    /**
886
     * Test set_sectionnum().
887
     *
888
     * @dataProvider set_sectionnum_provider
889
     * @covers ::set_sectionnum
890
     * @param int|null $sectionnum The section number
891
     * @param bool $nullexpected If null is expected
892
     * @param bool $exceptionexpected If an exception is expected
893
     */
894
    public function test_set_sectionnum(?int $sectionnum, bool $nullexpected = false, bool $exceptionexpected = false): void {
895
        $this->resetAfterTest();
896
 
897
        $generator = $this->getDataGenerator();
898
        $course = $generator->create_course(['numsections' => 2]);
899
        $format = course_get_format($course);
900
 
901
        if ($exceptionexpected) {
902
            $this->expectException(\coding_exception::class);
903
        }
904
        $format->set_sectionnum($sectionnum);
905
        if ($nullexpected) {
906
            $this->assertNull($format->get_sectionid());
907
            $this->assertNull($format->get_sectionnum());
908
        } else {
909
            $this->assertNotNull($format->get_sectionid());
910
            $this->assertNotNull($format->get_sectionnum());
911
        }
912
    }
913
 
914
    /**
915
     * Data provider for test_set_sectionnum.
916
     *
917
     * @return array The testing scenarios
918
     */
919
    public static function set_sectionnum_provider(): array {
920
        return [
921
            'General sectionnumber' => [
922
                'sectionnum' => 0,
923
                'nullexpected' => false,
924
            ],
925
            'Existing sectionnumber' => [
926
                'sectionnum' => 1,
927
                'nullexpected' => false,
928
            ],
929
            'Another existing sectionnumber' => [
930
                'sectionnum' => 2,
931
                'nullexpected' => false,
932
            ],
933
            'Null sectionnumber' => [
934
                'sectionnum' => null,
935
                'nullexpected' => true,
936
            ],
937
            'Invalid sectionnumber' => [
938
                'sectionnum' => 3,
939
                'nullexpected' => true,
940
                'exceptionexpected' => true,
941
            ],
942
            'Another invalid sectionnumber' => [
943
                'sectionnum' => -1,
944
                'nullexpected' => true,
945
                'exceptionexpected' => true,
946
            ],
947
        ];
948
    }
949
 
950
    /**
951
     * Test can_sections_be_removed_from_navigation().
952
     *
953
     * @covers ::can_sections_be_removed_from_navigation
954
     */
955
    public function test_can_sections_be_removed_from_navigation(): void {
956
        $this->resetAfterTest();
957
 
958
        $generator = $this->getDataGenerator();
959
 
960
        $course = $generator->create_course();
961
        $format = course_get_format($course);
962
        $this->assertFalse($format->can_sections_be_removed_from_navigation());
963
 
964
        $course = $generator->create_course(['format' => 'testformatsections']);
965
        $format = course_get_format($course);
966
        $this->assertTrue($format->can_sections_be_removed_from_navigation());
967
    }
968
}
969
 
970
/**
971
 * Class format_testformat.
972
 *
973
 * A test class that simulates a course format that doesn't define 'news_items' in default blocks.
974
 *
975
 * @copyright 2016 Jun Pataleta <jun@moodle.com>
976
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
977
 */
978
class format_testformat extends core_courseformat\base {
979
    /**
980
     * Returns the list of blocks to be automatically added for the newly created course.
981
     *
982
     * @return array
983
     */
984
    public function get_default_blocks() {
985
        return [
986
            BLOCK_POS_RIGHT => [],
987
            BLOCK_POS_LEFT => []
988
        ];
989
    }
990
}
991
 
992
/**
993
 * Class format_testformatsections.
994
 *
995
 * A test class that simulates a course format with sections.
996
 *
997
 * @package   core_courseformat
998
 * @copyright 2023 ISB Bayern
999
 * @author    Philipp Memmel
1000
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1001
 */
1002
class format_testformatsections extends core_courseformat\base {
1003
    /**
1004
     * Returns if this course format uses sections.
1005
     *
1006
     * @return true
1007
     */
1008
    public function uses_sections() {
1009
        return true;
1010
    }
1011
 
1012
    public function can_sections_be_removed_from_navigation(): bool {
1013
        return true;
1014
    }
1015
}
1016
 
1017
/**
1018
 * Class format_testlegacy.
1019
 *
1020
 * A test class that simulates old course formats that define 'news_items' in default blocks.
1021
 *
1022
 * @copyright 2016 Jun Pataleta <jun@moodle.com>
1023
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1024
 */
1025
class format_testlegacy extends core_courseformat\base {
1026
    /**
1027
     * Returns the list of blocks to be automatically added for the newly created course.
1028
     *
1029
     * @return array
1030
     */
1031
    public function get_default_blocks() {
1032
        return [
1033
            BLOCK_POS_RIGHT => ['news_items'],
1034
            BLOCK_POS_LEFT => []
1035
        ];
1036
    }
1037
}