AutorÃa | Ultima modificación | Ver Log |
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace qbank_managecategories\external;
use context;
use context_module;
use moodle_url;
use qbank_managecategories\question_categories;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../manage_category_test_base.php');
/**
* Unit tests for move_category
*
* @package qbank_managecategories
* @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net}
* @author Mark Johnson <mark.johnson@catalyst-eu.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \qbank_managecategories\external\move_category
*/
final class move_category_test extends \qbank_managecategories\manage_category_test_base {
/**
* Return order of categories for a given context.
*
* @param context $context The context to get the category order for.
* @return array Nested array, keyed by category IDs.
*/
private function get_current_order(context $context): array {
$categories = new question_categories(new moodle_url('/'), [$context]);
return $this->reduce_tree($categories->editlists[$context->id]->items);
}
/**
* Reduce the ordered tree of categories to a multi-dimensional array of IDs for easier comparison.
*
* @param array $tree Tree of categories from question_categories.
* @return array
*/
private function reduce_tree(array $tree): array {
$result = [];
foreach ($tree as $category) {
$result[$category->id] = [];
if (isset($category->children) && !empty((array)$category->children)) {
$result[$category->id] = $this->reduce_tree($category->children);
}
}
return $result;
}
/**
* Move a category below another category within the same parent.
*
* @return void
*/
public function test_move_category_down(): void {
$this->setAdminUser();
$this->resetAfterTest();
// Create context for question categories.
$course = $this->create_course();
$qbank = $this->create_qbank($course);
$context = context_module::instance($qbank->cmid);
$this->create_course_category();
// Question categories.
$qcat1 = question_get_default_category($context->id);
$qcat2 = $this->create_question_category_for_a_qbank($qbank);
$qcat3 = $this->create_question_category_for_a_qbank($qbank);
$this->assertEquals(999, $qcat1->sortorder);
$this->assertEquals(1000, $qcat2->sortorder);
$this->assertEquals(1001, $qcat3->sortorder);
// Check current order.
$currentorder = $this->get_current_order($context);
$expectedorder = [
$qcat1->id => [],
$qcat2->id => [],
$qcat3->id => [],
];
$this->assertEquals($expectedorder, $currentorder);
// Move category 1 after category 2.
$stateupdates = move_category::execute($context->id, $qcat1->id, $qcat1->parent, $qcat2->id);
$neworder = $this->get_current_order($context);
$newexpectedorder = [
$qcat2->id => [],
$qcat1->id => [],
$qcat3->id => [],
];
$this->assertEquals($newexpectedorder, $neworder);
// We should have an update to the sortorder of the moved category and following categories.
$expectedstateupdates = [
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat1->id,
'sortorder' => 1001,
],
],
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat3->id,
'sortorder' => 1002,
],
],
];
$this->assertEquals($stateupdates, $expectedstateupdates);
}
/**
* Move a category to the top of its parent.
*
* @return void
* @throws \moodle_exception
*/
public function test_move_category_to_top(): void {
$this->setAdminUser();
$this->resetAfterTest();
// Create context for question categories.
$qbank = $this->create_qbank($this->create_course());
$context = context_module::instance($qbank->cmid);
$this->create_course_category();
// Question categories.
$qcat1 = question_get_default_category($context->id);
$qcat2 = $this->create_question_category_for_a_qbank($qbank);
$qcat3 = $this->create_question_category_for_a_qbank($qbank);
// Check current order.
$currentorder = $this->get_current_order($context);
$expectedorder = [
$qcat1->id => [],
$qcat2->id => [],
$qcat3->id => [],
];
$this->assertEquals($expectedorder, $currentorder);
// Move category 3 to the top.
$stateupdates = move_category::execute($context->id, $qcat3->id, $qcat3->parent);
$neworder = $this->get_current_order($context);
$newexpectedorder = [
$qcat3->id => [],
$qcat1->id => [],
$qcat2->id => [],
];
$this->assertEquals($newexpectedorder, $neworder);
// Expecting all categories to have an updated sortorder.
$expectedstateupdates = [
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat3->id,
'sortorder' => 1,
],
],
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat1->id,
'sortorder' => 2,
],
],
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat2->id,
'sortorder' => 3,
],
],
];
$this->assertEquals($stateupdates, $expectedstateupdates);
}
/**
* Move a category to a new parent that doesn't currently have any children.
*
* @return void
*/
public function test_move_category_as_new_child(): void {
$this->setAdminUser();
$this->resetAfterTest();
// Create context for question categories.
$qbank = $this->create_qbank($this->create_course());
$context = context_module::instance($qbank->cmid);
$this->create_course_category();
// Question categories.
$qcat1 = question_get_default_category($context->id);
$qcat2 = $this->create_question_category_for_a_qbank($qbank);
$qcat3 = $this->create_question_category_for_a_qbank($qbank);
// Check current order.
$currentorder = $this->get_current_order($context);
$expectedorder = [
$qcat1->id => [],
$qcat2->id => [],
$qcat3->id => [],
];
$this->assertEquals($expectedorder, $currentorder);
// Set Category 2 as the parent of Category 1.
$stateupdates = move_category::execute($context->id, $qcat1->id, $qcat2->id);
$neworder = $this->get_current_order($context);
$newexpectedorder = [
$qcat2->id => [
$qcat1->id => [],
],
$qcat3->id => [],
];
$this->assertEquals($newexpectedorder, $neworder);
// Expecting an update to the parent and sortorder of the moved category, and updated sortorders for the children
// of the original parent.
$expectedstateupdates = [
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat1->id,
'sortorder' => 1,
'parent' => $qcat2->id,
],
],
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat2->id,
'sortorder' => 1,
],
],
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat3->id,
'sortorder' => 2,
],
],
];
$this->assertEquals($stateupdates, $expectedstateupdates);
}
/**
* Move a category from one parent to another that already has a child.
*
* @return void
* @throws \moodle_exception
*/
public function test_move_category_between_parents(): void {
$this->setAdminUser();
$this->resetAfterTest();
// Create context for question categories.
$qbank = $this->create_qbank($this->create_course());
$context = context_module::instance($qbank->cmid);
$this->create_course_category();
// Question categories.
$qcat1 = question_get_default_category($context->id);
$qcat2 = $this->create_question_category_for_a_qbank($qbank);
$qcat3 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat1->id]);
$qcat4 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat2->id]);
// Check current order.
$currentorder = $this->get_current_order($context);
$expectedorder = [
$qcat1->id => [
$qcat3->id => [],
],
$qcat2->id => [
$qcat4->id => [],
],
];
$this->assertEquals($expectedorder, $currentorder);
// Set Category 2 as the parent of Category 1.
$stateupdates = move_category::execute($context->id, $qcat3->id, $qcat2->id, $qcat4->id);
$neworder = $this->get_current_order($context);
$newexpectedorder = [
$qcat1->id => [],
$qcat2->id => [
$qcat4->id => [],
$qcat3->id => [],
],
];
$this->assertEquals($newexpectedorder, $neworder);
// As there are no remaining children of the original parent, and this was moved to the bottom of the new parent,
// just the moved category is updated.
$expectedstateupdates = [
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat3->id,
'sortorder' => 2,
'parent' => $qcat2->id,
],
],
];
$this->assertEquals($stateupdates, $expectedstateupdates);
}
/**
* Move a category that has its own children.
*
* The children should move with the parent.
*
* @return void
*/
public function test_move_category_with_children(): void {
$this->setAdminUser();
$this->resetAfterTest();
// Create context for question categories.
$qbank = $this->create_qbank($this->create_course());
$context = context_module::instance($qbank->cmid);
$this->create_course_category();
// Question categories.
$qcat1 = question_get_default_category($context->id);
$qcat2 = $this->create_question_category_for_a_qbank($qbank);
$qcat3 = $this->create_question_category_for_a_qbank($qbank);
$qcat4 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat3->id]);
$qcat5 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat3->id]);
// Check current order.
$currentorder = $this->get_current_order($context);
$expectedorder = [
$qcat1->id => [],
$qcat2->id => [],
$qcat3->id => [
$qcat4->id => [],
$qcat5->id => [],
],
];
$this->assertEquals($expectedorder, $currentorder);
$stateupdates = move_category::execute($context->id, $qcat3->id, $qcat3->parent, $qcat1->id);
$neworder = $this->get_current_order($context);
$newexpectedorder = [
$qcat1->id => [],
$qcat3->id => [
$qcat4->id => [],
$qcat5->id => [],
],
$qcat2->id => [],
];
$this->assertEquals($neworder, $newexpectedorder);
// Update the sortorder of the moving category and the following sibling. No updates to the children are required.
$expectedstateupdates = [
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat3->id,
'sortorder' => 1000,
],
],
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat2->id,
'sortorder' => 1001,
],
],
];
$this->assertEquals($stateupdates, $expectedstateupdates);
}
/**
* Move a category that has its own children to a new parent.
*
* The children should also move and become descendants of the new parent.
*
* @return void
*/
public function test_change_parent_with_children(): void {
$this->setAdminUser();
$this->resetAfterTest();
// Create context for question categories.
$qbank = $this->create_qbank($this->create_course());
$context = context_module::instance($qbank->cmid);
$this->create_course_category();
// Question categories.
$qcat1 = question_get_default_category($context->id);
$qcat2 = $this->create_question_category_for_a_qbank($qbank);
$qcat3 = $this->create_question_category_for_a_qbank($qbank);
$qcat4 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat3->id]);
$qcat5 = $this->create_question_category_for_a_qbank($qbank, ['parent' => $qcat4->id]);
// Check current order.
$currentorder = $this->get_current_order($context);
$expectedorder = [
$qcat1->id => [],
$qcat2->id => [],
$qcat3->id => [
$qcat4->id => [
$qcat5->id => [],
],
],
];
$this->assertEquals($expectedorder, $currentorder);
$stateupdates = move_category::execute($context->id, $qcat4->id, $qcat2->id);
$neworder = $this->get_current_order($context);
$newexpectedorder = [
$qcat1->id => [],
$qcat2->id => [
$qcat4->id => [
$qcat5->id => [],
],
],
$qcat3->id => [],
];
$this->assertEquals($newexpectedorder, $neworder);
// Expecting an update to the sortorder and parent of the moved category. No updates to the children are required.
$expectedstateupdates = [
(object)[
'name' => 'categories',
'action' => 'put',
'fields' => (object)[
'id' => $qcat4->id,
'parent' => $qcat2->id,
'sortorder' => 1,
],
],
];
$this->assertEquals($expectedstateupdates, $stateupdates);
}
}