Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 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
namespace qbank_managecategories\external;
18
 
19
use context;
20
use context_module;
21
use moodle_url;
22
use qbank_managecategories\question_categories;
23
 
24
defined('MOODLE_INTERNAL') || die();
25
 
26
require_once(__DIR__ . '/../manage_category_test_base.php');
27
 
28
/**
29
 * Unit tests for move_category
30
 *
31
 * @package qbank_managecategories
32
 * @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}
33
 * @author Mark Johnson <mark.johnson@catalyst-eu.net>
34
 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35
 * @covers \qbank_managecategories\external\move_category
36
 */
37
final class move_category_test extends \qbank_managecategories\manage_category_test_base {
38
 
39
    /**
40
     * Return order of categories for a given context.
41
     *
42
     * @param context $context The context to get the category order for.
43
     * @return array Nested array, keyed by category IDs.
44
     */
45
    private function get_current_order(context $context): array {
46
        $categories = new question_categories(new moodle_url('/'), [$context]);
47
        return $this->reduce_tree($categories->editlists[$context->id]->items);
48
    }
49
 
50
    /**
51
     * Reduce the ordered tree of categories to a multi-dimensional array of IDs for easier comparison.
52
     *
53
     * @param array $tree Tree of categories from question_categories.
54
     * @return array
55
     */
56
    private function reduce_tree(array $tree): array {
57
        $result = [];
58
        foreach ($tree as $category) {
59
            $result[$category->id] = [];
60
            if (isset($category->children) && !empty((array)$category->children)) {
61
                $result[$category->id] = $this->reduce_tree($category->children);
62
            }
63
        }
64
        return $result;
65
    }
66
 
67
    /**
68
     * Move a category below another category within the same parent.
69
     *
70
     * @return void
71
     */
72
    public function test_move_category_down(): void {
73
        $this->setAdminUser();
74
        $this->resetAfterTest();
75
 
76
        // Create context for question categories.
77
        $course = $this->create_course();
78
        $qbank = $this->create_qbank($course);
79
        $context = context_module::instance($qbank->cmid);
80
        $this->create_course_category();
81
 
82
        // Question categories.
83
        $qcat1 = question_get_default_category($context->id);
84
        $qcat2 = $this->create_question_category_for_a_qbank($qbank);
85
        $qcat3 = $this->create_question_category_for_a_qbank($qbank);
86
 
87
        $this->assertEquals(999, $qcat1->sortorder);
88
        $this->assertEquals(1000, $qcat2->sortorder);
89
        $this->assertEquals(1001, $qcat3->sortorder);
90
 
91
        // Check current order.
92
        $currentorder = $this->get_current_order($context);
93
        $expectedorder = [
94
            $qcat1->id => [],
95
            $qcat2->id => [],
96
            $qcat3->id => [],
97
        ];
98
        $this->assertEquals($expectedorder, $currentorder);
99
 
100
        // Move category 1 after category 2.
101
        $stateupdates = move_category::execute($context->id, $qcat1->id, $qcat1->parent, $qcat2->id);
102
 
103
        $neworder = $this->get_current_order($context);
104
        $newexpectedorder = [
105
            $qcat2->id => [],
106
            $qcat1->id => [],
107
            $qcat3->id => [],
108
        ];
109
        $this->assertEquals($newexpectedorder, $neworder);
110
 
111
        // We should have an update to the sortorder of the moved category and following categories.
112
        $expectedstateupdates = [
113
            (object)[
114
                'name' => 'categories',
115
                'action' => 'put',
116
                'fields' => (object)[
117
                    'id' => $qcat1->id,
118
                    'sortorder' => 1001,
119
                ],
120
            ],
121
            (object)[
122
                'name' => 'categories',
123
                'action' => 'put',
124
                'fields' => (object)[
125
                    'id' => $qcat3->id,
126
                    'sortorder' => 1002,
127
                ],
128
            ],
129
        ];
130
        $this->assertEquals($stateupdates, $expectedstateupdates);
131
    }
132
 
133
    /**
134
     * Move a category to the top of its parent.
135
     *
136
     * @return void
137
     * @throws \moodle_exception
138
     */
139
    public function test_move_category_to_top(): void {
140
        $this->setAdminUser();
141
        $this->resetAfterTest();
142
 
143
        // Create context for question categories.
144
        $qbank = $this->create_qbank($this->create_course());
145
        $context = context_module::instance($qbank->cmid);
146
        $this->create_course_category();
147
 
148
        // Question categories.
149
        $qcat1 = question_get_default_category($context->id);
150
        $qcat2 = $this->create_question_category_for_a_qbank($qbank);
151
        $qcat3 = $this->create_question_category_for_a_qbank($qbank);
152
 
153
        // Check current order.
154
        $currentorder = $this->get_current_order($context);
155
        $expectedorder = [
156
            $qcat1->id => [],
157
            $qcat2->id => [],
158
            $qcat3->id => [],
159
        ];
160
        $this->assertEquals($expectedorder, $currentorder);
161
 
162
        // Move category 3 to the top.
163
        $stateupdates = move_category::execute($context->id, $qcat3->id, $qcat3->parent);
164
 
165
        $neworder = $this->get_current_order($context);
166
        $newexpectedorder = [
167
            $qcat3->id => [],
168
            $qcat1->id => [],
169
            $qcat2->id => [],
170
        ];
171
        $this->assertEquals($newexpectedorder, $neworder);
172
 
173
        // Expecting all categories to have an updated sortorder.
174
        $expectedstateupdates = [
175
            (object)[
176
                'name' => 'categories',
177
                'action' => 'put',
178
                'fields' => (object)[
179
                    'id' => $qcat3->id,
180
                    'sortorder' => 1,
181
                ],
182
            ],
183
            (object)[
184
                'name' => 'categories',
185
                'action' => 'put',
186
                'fields' => (object)[
187
                    'id' => $qcat1->id,
188
                    'sortorder' => 2,
189
                ],
190
            ],
191
            (object)[
192
                'name' => 'categories',
193
                'action' => 'put',
194
                'fields' => (object)[
195
                    'id' => $qcat2->id,
196
                    'sortorder' => 3,
197
                ],
198
            ],
199
        ];
200
        $this->assertEquals($stateupdates, $expectedstateupdates);
201
    }
202
 
203
    /**
204
     * Move a category to a new parent that doesn't currently have any children.
205
     *
206
     * @return void
207
     */
208
    public function test_move_category_as_new_child(): void {
209
        $this->setAdminUser();
210
        $this->resetAfterTest();
211
 
212
        // Create context for question categories.
213
        $qbank = $this->create_qbank($this->create_course());
214
        $context = context_module::instance($qbank->cmid);
215
        $this->create_course_category();
216
 
217
        // Question categories.
218
        $qcat1 = question_get_default_category($context->id);
219
        $qcat2 = $this->create_question_category_for_a_qbank($qbank);
220
        $qcat3 = $this->create_question_category_for_a_qbank($qbank);
221
 
222
        // Check current order.
223
        $currentorder = $this->get_current_order($context);
224
        $expectedorder = [
225
            $qcat1->id => [],
226
            $qcat2->id => [],
227
            $qcat3->id => [],
228
        ];
229
        $this->assertEquals($expectedorder, $currentorder);
230
 
231
        // Set Category 2 as the parent of Category 1.
232
        $stateupdates = move_category::execute($context->id, $qcat1->id, $qcat2->id);
233
 
234
        $neworder = $this->get_current_order($context);
235
        $newexpectedorder = [
236
            $qcat2->id => [
237
                $qcat1->id => [],
238
            ],
239
            $qcat3->id => [],
240
        ];
241
        $this->assertEquals($newexpectedorder, $neworder);
242
 
243
        // Expecting an update to the parent and sortorder of the moved category, and updated sortorders for the children
244
        // of the original parent.
245
        $expectedstateupdates = [
246
            (object)[
247
                'name' => 'categories',
248
                'action' => 'put',
249
                'fields' => (object)[
250
                    'id' => $qcat1->id,
251
                    'sortorder' => 1,
252
                    'parent' => $qcat2->id,
253
                ],
254
            ],
255
            (object)[
256
                'name' => 'categories',
257
                'action' => 'put',
258
                'fields' => (object)[
259
                    'id' => $qcat2->id,
260
                    'sortorder' => 1,
261
                ],
262
            ],
263
            (object)[
264
                'name' => 'categories',
265
                'action' => 'put',
266
                'fields' => (object)[
267
                    'id' => $qcat3->id,
268
                    'sortorder' => 2,
269
                ],
270
            ],
271
        ];
272
        $this->assertEquals($stateupdates, $expectedstateupdates);
273
    }
274
 
275
    /**
276
     * Move a category from one parent to another that already has a child.
277
     *
278
     * @return void
279
     * @throws \moodle_exception
280
     */
281
    public function test_move_category_between_parents(): void {
282
        $this->setAdminUser();
283
        $this->resetAfterTest();
284
 
285
        // Create context for question categories.
286
        $qbank = $this->create_qbank($this->create_course());
287
        $context = context_module::instance($qbank->cmid);
288
        $this->create_course_category();
289
 
290
        // Question categories.
291
        $qcat1 = question_get_default_category($context->id);
292
        $qcat2 = $this->create_question_category_for_a_qbank($qbank);
293
        $qcat3 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat1->id]);
294
        $qcat4 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat2->id]);
295
 
296
        // Check current order.
297
        $currentorder = $this->get_current_order($context);
298
        $expectedorder = [
299
            $qcat1->id => [
300
                $qcat3->id => [],
301
            ],
302
            $qcat2->id => [
303
                $qcat4->id => [],
304
            ],
305
        ];
306
        $this->assertEquals($expectedorder, $currentorder);
307
 
308
        // Set Category 2 as the parent of Category 1.
309
        $stateupdates = move_category::execute($context->id, $qcat3->id, $qcat2->id, $qcat4->id);
310
 
311
        $neworder = $this->get_current_order($context);
312
        $newexpectedorder = [
313
            $qcat1->id => [],
314
            $qcat2->id => [
315
                $qcat4->id => [],
316
                $qcat3->id => [],
317
            ],
318
        ];
319
        $this->assertEquals($newexpectedorder, $neworder);
320
 
321
        // As there are no remaining children of the original parent, and this was moved to the bottom of the new parent,
322
        // just the moved category is updated.
323
        $expectedstateupdates = [
324
            (object)[
325
                'name' => 'categories',
326
                'action' => 'put',
327
                'fields' => (object)[
328
                    'id' => $qcat3->id,
329
                    'sortorder' => 2,
330
                    'parent' => $qcat2->id,
331
                ],
332
            ],
333
        ];
334
        $this->assertEquals($stateupdates, $expectedstateupdates);
335
    }
336
 
337
    /**
338
     * Move a category that has its own children.
339
     *
340
     * The children should move with the parent.
341
     *
342
     * @return void
343
     */
344
    public function test_move_category_with_children(): void {
345
        $this->setAdminUser();
346
        $this->resetAfterTest();
347
 
348
        // Create context for question categories.
349
        $qbank = $this->create_qbank($this->create_course());
350
        $context = context_module::instance($qbank->cmid);
351
        $this->create_course_category();
352
 
353
        // Question categories.
354
        $qcat1 = question_get_default_category($context->id);
355
        $qcat2 = $this->create_question_category_for_a_qbank($qbank);
356
        $qcat3 = $this->create_question_category_for_a_qbank($qbank);
357
        $qcat4 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat3->id]);
358
        $qcat5 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat3->id]);
359
 
360
        // Check current order.
361
        $currentorder = $this->get_current_order($context);
362
        $expectedorder = [
363
            $qcat1->id => [],
364
            $qcat2->id => [],
365
            $qcat3->id => [
366
                $qcat4->id => [],
367
                $qcat5->id => [],
368
            ],
369
        ];
370
        $this->assertEquals($expectedorder, $currentorder);
371
 
372
        $stateupdates = move_category::execute($context->id, $qcat3->id, $qcat3->parent, $qcat1->id);
373
 
374
        $neworder = $this->get_current_order($context);
375
        $newexpectedorder = [
376
            $qcat1->id => [],
377
            $qcat3->id => [
378
                $qcat4->id => [],
379
                $qcat5->id => [],
380
            ],
381
            $qcat2->id => [],
382
        ];
383
        $this->assertEquals($neworder, $newexpectedorder);
384
 
385
        // Update the sortorder of the moving category and the following sibling. No updates to the children are required.
386
        $expectedstateupdates = [
387
            (object)[
388
                'name' => 'categories',
389
                'action' => 'put',
390
                'fields' => (object)[
391
                    'id' => $qcat3->id,
392
                    'sortorder' => 1000,
393
                ],
394
            ],
395
            (object)[
396
                'name' => 'categories',
397
                'action' => 'put',
398
                'fields' => (object)[
399
                    'id' => $qcat2->id,
400
                    'sortorder' => 1001,
401
                ],
402
            ],
403
        ];
404
        $this->assertEquals($stateupdates, $expectedstateupdates);
405
    }
406
 
407
    /**
408
     * Move a category that has its own children to a new parent.
409
     *
410
     * The children should also move and become descendants of the new parent.
411
     *
412
     * @return void
413
     */
414
    public function test_change_parent_with_children(): void {
415
        $this->setAdminUser();
416
        $this->resetAfterTest();
417
 
418
        // Create context for question categories.
419
        $qbank = $this->create_qbank($this->create_course());
420
        $context = context_module::instance($qbank->cmid);
421
        $this->create_course_category();
422
 
423
        // Question categories.
424
        $qcat1 = question_get_default_category($context->id);
425
        $qcat2 = $this->create_question_category_for_a_qbank($qbank);
426
        $qcat3 = $this->create_question_category_for_a_qbank($qbank);
427
        $qcat4 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat3->id]);
428
        $qcat5 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat4->id]);
429
 
430
        // Check current order.
431
        $currentorder = $this->get_current_order($context);
432
        $expectedorder = [
433
            $qcat1->id => [],
434
            $qcat2->id => [],
435
            $qcat3->id => [
436
                $qcat4->id => [
437
                    $qcat5->id => [],
438
                ],
439
            ],
440
        ];
441
        $this->assertEquals($expectedorder, $currentorder);
442
 
443
        $stateupdates = move_category::execute($context->id, $qcat4->id, $qcat2->id);
444
 
445
        $neworder = $this->get_current_order($context);
446
        $newexpectedorder = [
447
            $qcat1->id => [],
448
            $qcat2->id => [
449
                $qcat4->id => [
450
                    $qcat5->id => [],
451
                ],
452
            ],
453
            $qcat3->id => [],
454
        ];
455
        $this->assertEquals($newexpectedorder, $neworder);
456
 
457
        // Expecting an update to the sortorder and parent of the moved category. No updates to the children are required.
458
        $expectedstateupdates = [
459
            (object)[
460
                'name' => 'categories',
461
                'action' => 'put',
462
                'fields' => (object)[
463
                    'id' => $qcat4->id,
464
                    'parent' => $qcat2->id,
465
                    'sortorder' => 1,
466
                ],
467
            ],
468
        ];
469
        $this->assertEquals($expectedstateupdates, $stateupdates);
470
    }
471
}