diff --git a/install/empty_data.php b/install/empty_data.php index 41b8ba69b46..e576b2082ba 100644 --- a/install/empty_data.php +++ b/install/empty_data.php @@ -9158,8 +9158,10 @@ public function getEmptyData(): array // Test environment data if ($is_testing) { $root_entity = array_filter($tables['glpi_entities'], static fn ($e) => $e['id'] === 0); - $e2e_entity = array_shift($root_entity); - $e2e_entity = array_replace($e2e_entity, [ + $root_entity = current($root_entity); + + // Main E2E test entity + $e2e_entity = array_replace($root_entity, [ 'id' => 1, 'name' => 'E2ETestEntity', 'entities_id' => 0, @@ -9168,6 +9170,26 @@ public function getEmptyData(): array ]); $tables['glpi_entities'][] = $e2e_entity; + // Sub entity 1 + $e2e_subentity1 = array_replace($root_entity, [ + 'id' => 2, + 'name' => 'E2ETestSubEntity1', + 'entities_id' => 1, + 'completename' => __('Root entity') . ' > E2ETestEntity > E2ETestSubEntity1', + 'level' => 3, + ]); + $tables['glpi_entities'][] = $e2e_subentity1; + + // Sub entity 2 + $e2e_subentity2 = array_replace($root_entity, [ + 'id' => 3, + 'name' => 'E2ETestSubEntity2', + 'entities_id' => 1, + 'completename' => __('Root entity') . ' > E2ETestEntity > E2ETestSubEntity2', + 'level' => 3, + ]); + $tables['glpi_entities'][] = $e2e_subentity2; + // New e2e super-admin user (login: e2e_tests, password: glpi) $default_glpi_user = array_filter($tables['glpi_users'], static fn ($u) => $u['id'] === self::USER_GLPI); $e2e_user = array_shift($default_glpi_user); diff --git a/package-lock.json b/package-lock.json index 1ecca8de9bc..1c710ed33ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,7 @@ "jquery": "^3.7.1", "jquery-migrate": "^3.5.2", "jquery-prettytextdiff": "^1.0.4", - "jquery-ui": "^1.13.3", + "jquery-ui": "^1.14.0", "jquery.fancytree": "^2.38.3", "jquery.rateit": "^1.1.6", "leaflet": "^1.9.4", @@ -10746,11 +10746,11 @@ } }, "node_modules/jquery-ui": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.13.3.tgz", - "integrity": "sha512-D2YJfswSJRh/B8M/zCowDpNFfwsDmtfnMPwjJTyvl+CBqzpYwQ+gFYIbUUlzijy/Qvoy30H1YhoSui4MNYpRwA==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/jquery-ui/-/jquery-ui-1.14.0.tgz", + "integrity": "sha512-mPfYKBoRCf0MzaT2cyW5i3IuZ7PfTITaasO5OFLAQxrHuI+ZxruPa+4/K1OMNT8oElLWGtIxc9aRbyw20BKr8g==", "dependencies": { - "jquery": ">=1.8.0 <4.0.0" + "jquery": ">=1.12.0 <5.0.0" } }, "node_modules/jquery.fancytree": { diff --git a/package.json b/package.json index 83c6b6ad639..9337bd94eeb 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "jquery": "^3.7.1", "jquery-migrate": "^3.5.2", "jquery-prettytextdiff": "^1.0.4", - "jquery-ui": "^1.13.3", + "jquery-ui": "^1.14.0", "jquery.fancytree": "^2.38.3", "jquery.rateit": "^1.1.6", "leaflet": "^1.9.4", diff --git a/phpunit/functional/DbUtilsTest.php b/phpunit/functional/DbUtilsTest.php index b8142e6ce76..f0221527bb2 100644 --- a/phpunit/functional/DbUtilsTest.php +++ b/phpunit/functional/DbUtilsTest.php @@ -574,6 +574,10 @@ public function testGetEntityRestrict() $this->login(); $instance = new \DbUtils(); + $root = getItemByTypeName('Entity', '_test_root_entity', true); + $child1 = getItemByTypeName('Entity', '_test_child_1', true); + $child2 = getItemByTypeName('Entity', '_test_child_2', true); + // See all, really all $_SESSION['glpishowallentities'] = 1; // will be restored by setEntity call @@ -593,23 +597,23 @@ public function testGetEntityRestrict() $this->setEntity('_test_root_entity', true); $this->assertSame( - "WHERE ( `glpi_computers`.`entities_id` IN ('2', '3', '4') ) ", + "WHERE ( `glpi_computers`.`entities_id` IN ('$root', '$child1', '$child2') ) ", $instance->getEntitiesRestrictRequest('WHERE', 'glpi_computers') ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => $instance->getEntitiesRestrictCriteria('glpi_computers')]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE `glpi_computers`.`entities_id` IN (\'2\', \'3\', \'4\')', + "SELECT * FROM `glpi_computers` WHERE `glpi_computers`.`entities_id` IN ('$root', '$child1', '$child2')", $it->getSql() ); //keep testing old method from db.function $this->assertSame( - "WHERE ( `glpi_computers`.`entities_id` IN ('2', '3', '4') ) ", + "WHERE ( `glpi_computers`.`entities_id` IN ('$root', '$child1', '$child2') ) ", getEntitiesRestrictRequest('WHERE', 'glpi_computers') ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => getEntitiesRestrictCriteria('glpi_computers')]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE (`glpi_computers`.`entities_id` IN (\'2\', \'3\', \'4\'))', + "SELECT * FROM `glpi_computers` WHERE (`glpi_computers`.`entities_id` IN ('$root', '$child1', '$child2'))", $it->getSql() ); @@ -617,23 +621,23 @@ public function testGetEntityRestrict() $this->setEntity('_test_root_entity', false); $this->assertSame( - "WHERE ( `glpi_computers`.`entities_id` IN ('2') ) ", + "WHERE ( `glpi_computers`.`entities_id` IN ('$root') ) ", $instance->getEntitiesRestrictRequest('WHERE', 'glpi_computers') ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => $instance->getEntitiesRestrictCriteria('glpi_computers')]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE `glpi_computers`.`entities_id` IN (\'2\')', + "SELECT * FROM `glpi_computers` WHERE `glpi_computers`.`entities_id` IN ('$root')", $it->getSql() ); //keep testing old method from db.function $this->assertSame( - "WHERE ( `glpi_computers`.`entities_id` IN ('2') ) ", + "WHERE ( `glpi_computers`.`entities_id` IN ('$root') ) ", getEntitiesRestrictRequest('WHERE', 'glpi_computers') ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => getEntitiesRestrictCriteria('glpi_computers')]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE (`glpi_computers`.`entities_id` IN (\'2\'))', + "SELECT * FROM `glpi_computers` WHERE (`glpi_computers`.`entities_id` IN ('$root'))", $it->getSql() ); @@ -641,45 +645,45 @@ public function testGetEntityRestrict() $this->setEntity('_test_child_1', false); $this->assertSame( - "WHERE ( `glpi_computers`.`entities_id` IN ('3') ) ", + "WHERE ( `glpi_computers`.`entities_id` IN ('$child1') ) ", $instance->getEntitiesRestrictRequest('WHERE', 'glpi_computers') ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => $instance->getEntitiesRestrictCriteria('glpi_computers')]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE `glpi_computers`.`entities_id` IN (\'3\')', + "SELECT * FROM `glpi_computers` WHERE `glpi_computers`.`entities_id` IN ('$child1')", $it->getSql() ); //keep testing old method from db.function $this->assertSame( - "WHERE ( `glpi_computers`.`entities_id` IN ('3') ) ", + "WHERE ( `glpi_computers`.`entities_id` IN ('$child1') ) ", getEntitiesRestrictRequest('WHERE', 'glpi_computers') ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => getEntitiesRestrictCriteria('glpi_computers')]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE (`glpi_computers`.`entities_id` IN (\'3\'))', + "SELECT * FROM `glpi_computers` WHERE (`glpi_computers`.`entities_id` IN ('$child1'))", $it->getSql() ); // Child without table $this->assertSame( - "WHERE ( `entities_id` IN ('3') ) ", + "WHERE ( `entities_id` IN ('$child1') ) ", $instance->getEntitiesRestrictRequest('WHERE') ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => $instance->getEntitiesRestrictCriteria()]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE `entities_id` IN (\'3\')', + "SELECT * FROM `glpi_computers` WHERE `entities_id` IN ('$child1')", $it->getSql() ); //keep testing old method from db.function $this->assertSame( - "WHERE ( `entities_id` IN ('3') ) ", + "WHERE ( `entities_id` IN ('$child1') ) ", getEntitiesRestrictRequest('WHERE') ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => getEntitiesRestrictCriteria()]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE (`entities_id` IN (\'3\'))', + "SELECT * FROM `glpi_computers` WHERE (`entities_id` IN ('$child1'))", $it->getSql() ); @@ -687,68 +691,68 @@ public function testGetEntityRestrict() $this->setEntity('_test_child_2', false); $this->assertSame( - "WHERE ( `glpi_computers`.`entities_id` IN ('4') OR (`glpi_computers`.`is_recursive`='1' AND `glpi_computers`.`entities_id` IN (0, 2)) ) ", + "WHERE ( `glpi_computers`.`entities_id` IN ('$child2') OR (`glpi_computers`.`is_recursive`='1' AND `glpi_computers`.`entities_id` IN (0, $root)) ) ", $instance->getEntitiesRestrictRequest('WHERE', 'glpi_computers', '', '', true) ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => $instance->getEntitiesRestrictCriteria('glpi_computers', '', '', true)]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE (`glpi_computers`.`entities_id` IN (\'4\') OR (`glpi_computers`.`is_recursive` = \'1\' AND `glpi_computers`.`entities_id` IN (\'0\', \'2\')))', + "SELECT * FROM `glpi_computers` WHERE (`glpi_computers`.`entities_id` IN ('$child2') OR (`glpi_computers`.`is_recursive` = '1' AND `glpi_computers`.`entities_id` IN ('0', '$root')))", $it->getSql() ); //keep testing old method from db.function $this->assertSame( - "WHERE ( `glpi_computers`.`entities_id` IN ('4') OR (`glpi_computers`.`is_recursive`='1' AND `glpi_computers`.`entities_id` IN (0, 2)) ) ", + "WHERE ( `glpi_computers`.`entities_id` IN ('$child2') OR (`glpi_computers`.`is_recursive`='1' AND `glpi_computers`.`entities_id` IN (0, $root)) ) ", getEntitiesRestrictRequest('WHERE', 'glpi_computers', '', '', true) ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => getEntitiesRestrictCriteria('glpi_computers', '', '', true)]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE ((`glpi_computers`.`entities_id` IN (\'4\') OR (`glpi_computers`.`is_recursive` = \'1\' AND `glpi_computers`.`entities_id` IN (\'0\', \'2\'))))', + "SELECT * FROM `glpi_computers` WHERE ((`glpi_computers`.`entities_id` IN ('$child2') OR (`glpi_computers`.`is_recursive` = '1' AND `glpi_computers`.`entities_id` IN ('0', '$root'))))", $it->getSql() ); //Child + parent on glpi_entities $it->execute(['FROM' => 'glpi_entities', 'WHERE' => $instance->getEntitiesRestrictCriteria('glpi_entities', '', '', true)]); $this->assertSame( - 'SELECT * FROM `glpi_entities` WHERE (`glpi_entities`.`id` IN (\'4\', \'0\', \'2\'))', + "SELECT * FROM `glpi_entities` WHERE (`glpi_entities`.`id` IN ('$child2', '0', '$root'))", $it->getSql() ); //keep testing old method from db.function $it->execute(['FROM' => 'glpi_entities', 'WHERE' => getEntitiesRestrictCriteria('glpi_entities', '', '', true)]); $this->assertSame( - 'SELECT * FROM `glpi_entities` WHERE ((`glpi_entities`.`id` IN (\'4\', \'0\', \'2\')))', + "SELECT * FROM `glpi_entities` WHERE ((`glpi_entities`.`id` IN ('$child2', '0', '$root')))", $it->getSql() ); //Child + parent -- automatic recusrivity detection $it->execute(['FROM' => 'glpi_computers', 'WHERE' => $instance->getEntitiesRestrictCriteria('glpi_computers', '', '', 'auto')]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE (`glpi_computers`.`entities_id` IN (\'4\') OR (`glpi_computers`.`is_recursive` = \'1\' AND `glpi_computers`.`entities_id` IN (\'0\', \'2\')))', + "SELECT * FROM `glpi_computers` WHERE (`glpi_computers`.`entities_id` IN ('$child2') OR (`glpi_computers`.`is_recursive` = '1' AND `glpi_computers`.`entities_id` IN ('0', '$root')))", $it->getSql() ); //keep testing old method from db.function $it->execute(['FROM' => 'glpi_computers', 'WHERE' => getEntitiesRestrictCriteria('glpi_computers', '', '', 'auto')]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE ((`glpi_computers`.`entities_id` IN (\'4\') OR (`glpi_computers`.`is_recursive` = \'1\' AND `glpi_computers`.`entities_id` IN (\'0\', \'2\'))))', + "SELECT * FROM `glpi_computers` WHERE ((`glpi_computers`.`entities_id` IN ('$child2') OR (`glpi_computers`.`is_recursive` = '1' AND `glpi_computers`.`entities_id` IN ('0', '$root'))))", $it->getSql() ); // Child + parent without table $this->assertSame( - "WHERE ( `entities_id` IN ('4') OR (`is_recursive`='1' AND `entities_id` IN (0, 2)) ) ", + "WHERE ( `entities_id` IN ('$child2') OR (`is_recursive`='1' AND `entities_id` IN (0, $root)) ) ", $instance->getEntitiesRestrictRequest('WHERE', '', '', '', true) ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => $instance->getEntitiesRestrictCriteria('', '', '', true)]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE (`entities_id` IN (\'4\') OR (`is_recursive` = \'1\' AND `entities_id` IN (\'0\', \'2\')))', + "SELECT * FROM `glpi_computers` WHERE (`entities_id` IN ('$child2') OR (`is_recursive` = '1' AND `entities_id` IN ('0', '$root')))", $it->getSql() ); - $it->execute(['FROM' => 'glpi_entities', 'WHERE' => $instance->getEntitiesRestrictCriteria('glpi_entities', '', 3, true)]); + $it->execute(['FROM' => 'glpi_entities', 'WHERE' => $instance->getEntitiesRestrictCriteria('glpi_entities', '', $child1, true)]); $this->assertSame( - 'SELECT * FROM `glpi_entities` WHERE (`glpi_entities`.`id` IN (\'3\', \'0\', \'2\'))', + "SELECT * FROM `glpi_entities` WHERE (`glpi_entities`.`id` IN ('$child1', '0', '$root'))", $it->getSql() ); @@ -760,18 +764,18 @@ public function testGetEntityRestrict() //keep testing old method from db.function $this->assertSame( - "WHERE ( `entities_id` IN ('4') OR (`is_recursive`='1' AND `entities_id` IN (0, 2)) ) ", + "WHERE ( `entities_id` IN ('$child2') OR (`is_recursive`='1' AND `entities_id` IN (0, $root)) ) ", getEntitiesRestrictRequest('WHERE', '', '', '', true) ); $it->execute(['FROM' => 'glpi_computers', 'WHERE' => getEntitiesRestrictCriteria('', '', '', true)]); $this->assertSame( - 'SELECT * FROM `glpi_computers` WHERE ((`entities_id` IN (\'4\') OR (`is_recursive` = \'1\' AND `entities_id` IN (\'0\', \'2\'))))', + "SELECT * FROM `glpi_computers` WHERE ((`entities_id` IN ('$child2') OR (`is_recursive` = '1' AND `entities_id` IN ('0', '$root'))))", $it->getSql() ); - $it->execute(['FROM' => 'glpi_entities', 'WHERE' => getEntitiesRestrictCriteria('glpi_entities', '', 3, true)]); + $it->execute(['FROM' => 'glpi_entities', 'WHERE' => getEntitiesRestrictCriteria('glpi_entities', '', $child1, true)]); $this->assertSame( - 'SELECT * FROM `glpi_entities` WHERE ((`glpi_entities`.`id` IN (\'3\', \'0\', \'2\')))', + "SELECT * FROM `glpi_entities` WHERE ((`glpi_entities`.`id` IN ('$child1', '0', '$root')))", $it->getSql() ); @@ -823,7 +827,7 @@ private function runGetAncestorsOf($cache = false, $hit = false) } //test on ent1 - $expected = [0 => 0, 2 => $ent0]; + $expected = [0 => 0, $ent0 => $ent0]; if ($cache === true && $hit === false) { $this->assertFalse($GLPI_CACHE->has($ckey_ent1)); } else if ($cache === true && $hit === true) { @@ -838,7 +842,7 @@ private function runGetAncestorsOf($cache = false, $hit = false) } //test on ent2 - $expected = [0 => 0, 2 => $ent0]; + $expected = [0 => 0, $ent0 => $ent0]; if ($cache === true && $hit === false) { $this->assertFalse($GLPI_CACHE->has($ckey_ent2)); } else if ($cache === true && $hit === true) { @@ -1432,6 +1436,8 @@ public function testGetDateCriteriaError2() public static function autoNameProvider() { + $test_child_1 = getItemByTypeName('Entity', '_test_child_1', true); + return [ //will return name without changes [ @@ -1464,7 +1470,7 @@ public static function autoNameProvider() 'field' => 'name', 'is_template' => true, 'itemtype' => 'Computer', - 'entities_id' => 3, + 'entities_id' => $test_child_1, 'expected' => '_test_pc14' ], [ // not existing on entity, not sanitized, and containing a special char @@ -1472,7 +1478,7 @@ public static function autoNameProvider() 'field' => 'name', 'is_template' => true, 'itemtype' => 'Computer', - 'entities_id' => 3, + 'entities_id' => $test_child_1, 'expected' => 'pc_>_01' ], ]; diff --git a/phpunit/functional/EntityTest.php b/phpunit/functional/EntityTest.php index e216fcd3899..d77f37221a9 100644 --- a/phpunit/functional/EntityTest.php +++ b/phpunit/functional/EntityTest.php @@ -249,7 +249,7 @@ private function checkParentsSonsAreReset() $ent1 = getItemByTypeName('Entity', '_test_child_1', true); $ent2 = getItemByTypeName('Entity', '_test_child_2', true); - $expected = [0 => 0, 2 => $ent0]; + $expected = [0 => 0, $ent0 => $ent0]; $ancestors = getAncestorsOf('glpi_entities', $ent1); $this->assertSame($expected, $ancestors); @@ -1204,6 +1204,8 @@ public function testRename() public static function entityTreeProvider(): iterable { $e2e_test_root = getItemByTypeName('Entity', 'E2ETestEntity', true); + $e2e_test_child1 = getItemByTypeName('Entity', 'E2ETestSubEntity1', true); + $e2e_test_child2 = getItemByTypeName('Entity', 'E2ETestSubEntity2', true); $entity_test_root = getItemByTypeName('Entity', '_test_root_entity'); $entity_test_child_1 = getItemByTypeName('Entity', '_test_child_1'); $entity_test_child_2 = getItemByTypeName('Entity', '_test_child_2'); @@ -1216,7 +1218,16 @@ public static function entityTreeProvider(): iterable 'tree' => [ $e2e_test_root => [ 'name' => 'E2ETestEntity', - 'tree' => [] + 'tree' => [ + $e2e_test_child1 => [ + 'name' => 'E2ETestSubEntity1', + 'tree' => [], + ], + $e2e_test_child2 => [ + 'name' => 'E2ETestSubEntity2', + 'tree' => [], + ], + ], ], $entity_test_root->getID() => [ 'name' => $entity_test_root->fields['name'], diff --git a/phpunit/functional/ReminderTest.php b/phpunit/functional/ReminderTest.php index 586445e2278..874c006c86d 100644 --- a/phpunit/functional/ReminderTest.php +++ b/phpunit/functional/ReminderTest.php @@ -43,6 +43,15 @@ class ReminderTest extends DbTestCase { public function testAddVisibilityRestrict() { + $e2e_root = getItemByTypeName('Entity', 'E2ETestEntity', true); + $e2e_test_child1 = getItemByTypeName('Entity', 'E2ETestSubEntity1', true); + $e2e_test_child2 = getItemByTypeName('Entity', 'E2ETestSubEntity2', true); + $test_root = getItemByTypeName('Entity', '_test_root_entity', true); + $test_child_1 = getItemByTypeName('Entity', '_test_child_1', true); + $test_child_2 = getItemByTypeName('Entity', '_test_child_2', true); + + $all_entities = "'0', '$test_root', '$e2e_root', '$e2e_test_child1', '$e2e_test_child2', '$test_child_1', '$test_child_2'"; + //first, as a super-admin $this->login(); $users_id = \Session::getLoginUserID(); @@ -50,10 +59,10 @@ public function testAddVisibilityRestrict() OR `glpi_reminders_users`.`users_id` = '$users_id' OR (`glpi_profiles_reminders`.`profiles_id` = '4' AND (`glpi_profiles_reminders`.`no_entity_restriction` = '1' - OR ((`glpi_profiles_reminders`.`entities_id` IN ('2', '3', '4') + OR ((`glpi_profiles_reminders`.`entities_id` IN ('$test_root', '$test_child_1', '$test_child_2') OR (`glpi_profiles_reminders`.`is_recursive` = '1' AND `glpi_profiles_reminders`.`entities_id` IN ('0')))))) - OR ((`glpi_entities_reminders`.`entities_id` IN ('2', '3', '4') + OR ((`glpi_entities_reminders`.`entities_id` IN ('$test_root', '$test_child_1', '$test_child_2') OR (`glpi_entities_reminders`.`is_recursive` = '1' AND `glpi_entities_reminders`.`entities_id` IN ('0')))))"); $this->assertSame( @@ -66,8 +75,8 @@ public function testAddVisibilityRestrict() OR `glpi_reminders_users`.`users_id` = '5' OR (`glpi_profiles_reminders`.`profiles_id` = '2' AND (`glpi_profiles_reminders`.`no_entity_restriction` = '1' - OR (`glpi_profiles_reminders`.`entities_id` IN ('0', '2', '1', '3', '4')))) - OR (`glpi_entities_reminders`.`entities_id` IN ('0', '2', '1', '3', '4')))"); + OR (`glpi_profiles_reminders`.`entities_id` IN ($all_entities)))) + OR (`glpi_entities_reminders`.`entities_id` IN ($all_entities)))"); $this->assertSame( $expected, trim(preg_replace('/\s+/', ' ', \Reminder::addVisibilityRestrict())) @@ -78,8 +87,8 @@ public function testAddVisibilityRestrict() OR `glpi_reminders_users`.`users_id` = '4' OR (`glpi_profiles_reminders`.`profiles_id` = '6' AND (`glpi_profiles_reminders`.`no_entity_restriction` = '1' - OR (`glpi_profiles_reminders`.`entities_id` IN ('0', '2', '1', '3', '4')))) - OR (`glpi_entities_reminders`.`entities_id` IN ('0', '2', '1', '3', '4')))"); + OR (`glpi_profiles_reminders`.`entities_id` IN ($all_entities)))) + OR (`glpi_entities_reminders`.`entities_id` IN ($all_entities)))"); $this->assertSame( $expected, trim(preg_replace('/\s+/', ' ', \Reminder::addVisibilityRestrict())) @@ -93,11 +102,11 @@ public function testAddVisibilityRestrict() OR `glpi_reminders_users`.`users_id` = '4' OR (`glpi_groups_reminders`.`groups_id` IN ('42', '1337') AND (`glpi_groups_reminders`.`no_entity_restriction` = '1' - OR (`glpi_groups_reminders`.`entities_id` IN ('0', '2', '1', '3', '4')))) + OR (`glpi_groups_reminders`.`entities_id` IN ($all_entities)))) OR (`glpi_profiles_reminders`.`profiles_id` = '6' AND (`glpi_profiles_reminders`.`no_entity_restriction` = '1' - OR (`glpi_profiles_reminders`.`entities_id` IN ('0', '2', '1', '3', '4')))) - OR (`glpi_entities_reminders`.`entities_id` IN ('0', '2', '1', '3', '4')))"); + OR (`glpi_profiles_reminders`.`entities_id` IN ($all_entities)))) + OR (`glpi_entities_reminders`.`entities_id` IN ($all_entities)))"); $this->assertSame( $expected, trim(preg_replace('/\s+/', ' ', $str)) diff --git a/phpunit/functional/SavedSearchTest.php b/phpunit/functional/SavedSearchTest.php index 9175fa0decd..2bb49fb48d9 100644 --- a/phpunit/functional/SavedSearchTest.php +++ b/phpunit/functional/SavedSearchTest.php @@ -59,6 +59,10 @@ public function testGetVisibilityCriteria() public function testAddVisibilityRestrict() { + $test_root = getItemByTypeName('Entity', '_test_root_entity', true); + $test_child_1 = getItemByTypeName('Entity', '_test_child_1', true); + $test_child_2 = getItemByTypeName('Entity', '_test_child_2', true); + //first, as a super-admin $this->login(); $this->assertSame('', \SavedSearch::addVisibilityRestrict()); @@ -91,7 +95,7 @@ public function testAddVisibilityRestrict() // Check entity restriction $this->setEntity('_test_root_entity', true); $this->assertSame( - "((`glpi_savedsearches`.`is_private` = '1' AND `glpi_savedsearches`.`users_id` = '5') OR (`glpi_savedsearches`.`is_private` = '0')) AND ((`glpi_savedsearches`.`entities_id` IN ('2', '3', '4') OR (`glpi_savedsearches`.`is_recursive` = '1' AND `glpi_savedsearches`.`entities_id` IN ('0'))))", + "((`glpi_savedsearches`.`is_private` = '1' AND `glpi_savedsearches`.`users_id` = '5') OR (`glpi_savedsearches`.`is_private` = '0')) AND ((`glpi_savedsearches`.`entities_id` IN ('$test_root', '$test_child_1', '$test_child_2') OR (`glpi_savedsearches`.`is_recursive` = '1' AND `glpi_savedsearches`.`entities_id` IN ('0'))))", \SavedSearch::addVisibilityRestrict() ); } diff --git a/phpunit/functional/SearchTest.php b/phpunit/functional/SearchTest.php index d9ed138468b..fc9307251c6 100644 --- a/phpunit/functional/SearchTest.php +++ b/phpunit/functional/SearchTest.php @@ -355,6 +355,10 @@ public function testFlagMetaComputerUser() public function testNestedAndMetaComputer() { + $test_root = getItemByTypeName('Entity', '_test_root_entity', true); + $test_child_1 = getItemByTypeName('Entity', '_test_child_1', true); + $test_child_2 = getItemByTypeName('Entity', '_test_child_2', true); + $search_params = [ 'reset' => 'reset', 'is_deleted' => 0, @@ -451,7 +455,7 @@ public function testNestedAndMetaComputer() $contains = [ "`glpi_computers`.`is_deleted` = 0", "AND `glpi_computers`.`is_template` = 0", - "`glpi_computers`.`entities_id` IN ('2', '3', '4')", + "`glpi_computers`.`entities_id` IN ('$test_root', '$test_child_1', '$test_child_2')", "OR (`glpi_computers`.`is_recursive`='1' AND `glpi_computers`.`entities_id` IN (0))", "`glpi_computers`.`name` LIKE '%test%'", "AND `glpi_softwares`.`id` = '10784'", @@ -471,6 +475,10 @@ public function testNestedAndMetaComputer() public function testViewCriterion() { + $test_root = getItemByTypeName('Entity', '_test_root_entity', true); + $test_child_1 = getItemByTypeName('Entity', '_test_child_1', true); + $test_child_2 = getItemByTypeName('Entity', '_test_child_2', true); + $data = $this->doSearch('Computer', [ 'reset' => 'reset', 'is_deleted' => 0, @@ -491,7 +499,7 @@ public function testViewCriterion() $contains = [ "`glpi_computers`.`is_deleted` = 0", "AND `glpi_computers`.`is_template` = 0", - "`glpi_computers`.`entities_id` IN ('2', '3', '4')", + "`glpi_computers`.`entities_id` IN ('$test_root', '$test_child_1', '$test_child_2')", "OR (`glpi_computers`.`is_recursive`='1' AND `glpi_computers`.`entities_id` IN (0))" ]; @@ -2528,6 +2536,10 @@ public function testSearchWithMultipleFkeysOnSameTable() public function testSearchAllAssets() { + $test_root = getItemByTypeName('Entity', '_test_root_entity', true); + $test_child_1 = getItemByTypeName('Entity', '_test_child_1', true); + $test_child_2 = getItemByTypeName('Entity', '_test_child_2', true); + $data = $this->doSearch('AllAssets', [ 'reset' => 'reset', 'is_deleted' => 0, @@ -2571,7 +2583,7 @@ public function testSearchAllAssets() $data['sql']['search'] ); $this->assertStringContainsString( - "`$type`.`entities_id` IN ('2', '3', '4')", + "`$type`.`entities_id` IN ('$test_root', '$test_child_1', '$test_child_2')", $data['sql']['search'] ); $this->assertStringContainsString( diff --git a/templates/layout/parts/profile_selector.html.twig b/templates/layout/parts/profile_selector.html.twig index a56e7e45c46..2f8e12bbf4b 100644 --- a/templates/layout/parts/profile_selector.html.twig +++ b/templates/layout/parts/profile_selector.html.twig @@ -102,7 +102,7 @@ title="{{ __("Clear search") }}" data-bs-toggle="tooltip" data-bs-placement="top"> - @@ -256,9 +256,9 @@ hotkeys('ctrl+alt+e, option+command+e', async function(e) { e.stopPropagation(); e.preventDefault(); - $('.user-menu-dropdown-toggle').dropdown('show'); + $('.user-menu-dropdown-toggle:visible').dropdown('show'); await new Promise(r => setTimeout(r, 100)); - $('.entity-dropdown-toggle').dropdown('show'); + $('.user-menu-dropdown-toggle:visible').parent().find('.entity-dropdown-toggle').dropdown('show'); $('input[name=entsearchtext]').filter(":visible")[0].focus(); }); }); diff --git a/tests/cypress/e2e/entities_selector.cy.js b/tests/cypress/e2e/entities_selector.cy.js new file mode 100644 index 00000000000..0ada7e8ad9d --- /dev/null +++ b/tests/cypress/e2e/entities_selector.cy.js @@ -0,0 +1,104 @@ +/** + * --------------------------------------------------------------------- + * + * GLPI - Gestionnaire Libre de Parc Informatique + * + * http://glpi-project.org + * + * @copyright 2015-2024 Teclib' and contributors. + * @copyright 2003-2014 by the INDEPNET Development Team. + * @licence https://www.gnu.org/licenses/gpl-3.0.html + * + * --------------------------------------------------------------------- + * + * LICENSE + * + * This file is part of GLPI. + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * --------------------------------------------------------------------- + */ +describe('Entities selector', () => { + beforeEach(() => { + cy.login(); + cy.changeProfile('Super-Admin', true); + + // Go to any page; force the entity to be E2ETestEntity + cy.visit('/front/central.php?active_entity=1'); + cy.get('header').findByTitle('Root entity > E2ETestEntity').should('exist'); + }); + + after(() => { + cy.visit('/front/central.php?active_entity=1&is_recursive=1'); + }); + + it('Can switch to full structure', () => { + cy.openEntitySelector(); + + // Enable full structure + cy.get('header').findByTitle('Root entity > E2ETestEntity (full structure)').should('not.exist'); + cy.findByRole("button", {'name': "Select all"}).click(); + cy.get('header').findByTitle('Root entity > E2ETestEntity (full structure)').should('exist'); + }); + + it('Can search for entities', () => { + cy.openEntitySelector(); + + // Search for a specific entity + cy.findByRole('gridcell', {'name': "E2ETestSubEntity1"}).should('not.exist'); + cy.findByRole('gridcell', {'name': "E2ETestSubEntity2"}).should('not.exist'); + cy.focused().type("Entity2"); + cy.findByRole("button", {'name': "Search"}).click(); + cy.findByRole('gridcell', {'name': "E2ETestSubEntity1"}).should('not.exist'); + cy.findByRole('gridcell', {'name': "E2ETestSubEntity2"}).should('exist'); + + // Go to entity 2 + cy.get('header').findByTitle('Root entity > E2ETestEntity > E2ETestSubEntity2').should('not.exist'); + cy.findByRole('gridcell', {'name': "E2ETestSubEntity2"}) + .findByRole('link', {'name': "E2ETestSubEntity2"}) + .as("entity_link") + .click() + ; + cy.get('@entity_link').click(); // Not sure why but the link only work in cypress if you click twice... + cy.get('header').findByTitle('Root entity > E2ETestEntity > E2ETestSubEntity2').should('exist'); + }); + + it('Can fold/unfold tree', () => { + cy.openEntitySelector(); + + // Unfold the tree + cy.findByRole('gridcell', {'name': "E2ETestSubEntity1"}).should('not.exist'); + cy.findByRole('gridcell', {'name': "E2ETestSubEntity2"}).should('not.exist'); + cy.get('.fancytree-expander[role=button]:visible').as('toggle_tree').click(); // We don't have access to a more precise selector here, so we have to rely on css + cy.findByRole('gridcell', {'name': "E2ETestSubEntity1"}).should('exist'); + cy.findByRole('gridcell', {'name': "E2ETestSubEntity2"}).should('exist'); + + // Fold tree again + cy.get('@toggle_tree').click(); + cy.findByRole('gridcell', {'name': "E2ETestSubEntity1"}).should('not.exist'); + cy.findByRole('gridcell', {'name': "E2ETestSubEntity2"}).should('not.exist'); + }); + + it('Can enable sub entities', { retries: {runMode: 2, openMode: 0} }, () => { + cy.openEntitySelector(); + + // Enable sub entities + cy.get('header').findByTitle('Root entity > E2ETestEntity (tree structure)').should('not.exist'); + cy.findByRole('gridcell', {'name': "Root entity > E2ETestEntity+ sub-entities"}) + .findByLabelText('+ sub-entities') + .click(); + cy.get('header').findByTitle('Root entity > E2ETestEntity (tree structure)').should('exist'); + }); +}); diff --git a/tests/cypress/e2e/fileupload.cy.js b/tests/cypress/e2e/fileupload.cy.js new file mode 100644 index 00000000000..6d39be18926 --- /dev/null +++ b/tests/cypress/e2e/fileupload.cy.js @@ -0,0 +1,56 @@ +/** + * --------------------------------------------------------------------- + * + * GLPI - Gestionnaire Libre de Parc Informatique + * + * http://glpi-project.org + * + * @copyright 2015-2024 Teclib' and contributors. + * @copyright 2003-2014 by the INDEPNET Development Team. + * @licence https://www.gnu.org/licenses/gpl-3.0.html + * + * --------------------------------------------------------------------- + * + * LICENSE + * + * This file is part of GLPI. + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * --------------------------------------------------------------------- + */ +describe('File upload', () => { + beforeEach(() => { + cy.login(); + cy.changeProfile('Super-Admin', true); + cy.visit('/front/document.form.php'); + }); + + it('Can upload file', () => { + // Upload file + cy.get("input[type=file]").selectFile("fixtures/uploads/bar.txt"); + cy.findByText('Upload successful').should('exist'); + cy.findByRole("button", {'name': "Add"}).click(); + cy.findByRole('textbox', {'name': "Name"}).should('have.value', 'bar.txt'); + + // Download file + cy.get('#main-form') + .findByRole('link', {'name': "bar.txt"}) + .invoke('attr', 'target', '_self') // Cypress don't like new tabs + .click(); + cy.readFile('cypress/downloads/bar.txt').then(content => { + cy.wrap('bar').should('eq', content); + }); + }); +}); diff --git a/tests/cypress/support/commands.d.ts b/tests/cypress/support/commands.d.ts index 243cfffc687..d2c0f6f804d 100644 --- a/tests/cypress/support/commands.d.ts +++ b/tests/cypress/support/commands.d.ts @@ -33,6 +33,7 @@ declare namespace Cypress { interface Chainable { + openEntitySelector(): Chainable startToDrag(): Chainable dropDraggedItemAfter(): Chainable checkAndCloseAlert(text: string): Chainable diff --git a/tests/cypress/support/commands.js b/tests/cypress/support/commands.js index 34a1cea898b..f20a30543ab 100644 --- a/tests/cypress/support/commands.js +++ b/tests/cypress/support/commands.js @@ -401,6 +401,16 @@ Cypress.Commands.add('disableDebugMode', () => { }); }); +Cypress.Commands.add('openEntitySelector', () => { + cy.intercept('GET', '/ajax/entitytreesons.php*').as('load_data_request'); + + cy.findAllByText('Select the desired entity').should('not.be.visible'); + cy.get('body').type('{ctrl}{alt}e'); + cy.findAllByText('Select the desired entity').should('be.visible'); + + cy.wait('@load_data_request'); +}); + // The "startToDrag" and "dropDraggedItemAfter" commands are not perfect as they // simulate dragging by moving the DOM node using jquery. //