Initial Drupal 11 with DDEV setup

This commit is contained in:
gluebox
2025-10-08 11:39:17 -04:00
commit 89ef74b305
25344 changed files with 2599172 additions and 0 deletions

View File

@ -0,0 +1,4 @@
name: 'Tests menu link access'
type: module
package: Testing
version: VERSION

View File

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Drupal\menu_link_access_test\Hook;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Hook\Attribute\Hook;
/**
* Hook implementations for menu_link_access_test.
*/
class MenuLinkAccessTestHooks {
/**
* Implements hook_ENTITY_TYPE_access().
*/
#[Hook('menu_link_content_access')]
public function entityTestAccess(EntityInterface $entity, $operation, AccountInterface $account): AccessResultInterface {
if (in_array($operation, ['update', 'delete'])) {
return AccessResult::forbidden();
}
return AccessResult::neutral();
}
/**
* Implements hook_ENTITY_TYPE_create_access().
*/
#[Hook('menu_link_content_create_access')]
public function entityTestCreateAccess(AccountInterface $account, $context, $entity_bundle): AccessResultInterface {
return AccessResult::forbidden();
}
}

View File

@ -0,0 +1,4 @@
name: 'Tests MenuLinkAdd class'
type: module
package: Testing
version: VERSION

View File

@ -0,0 +1,6 @@
entity.menu.add_link_form_deprecated:
route_name: entity.menu.add_link_form
title: 'Add link (deprecated)'
class: \Drupal\menu_ui\Plugin\Menu\LocalAction\MenuLinkAdd
appears_on:
- entity.menu.edit_form

View File

@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Functional;
use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
/**
* Generic module test for menu_ui.
*
* @group menu_ui
*/
class GenericTest extends GenericModuleTestBase {}

View File

@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Functional;
use Drupal\Core\Url;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\Tests\system\Functional\Cache\PageCacheTagsTestBase;
use Drupal\system\Entity\Menu;
/**
* Tests the Menu and Menu Link entities' cache tags.
*
* @group menu_ui
*/
class MenuCacheTagsTest extends PageCacheTagsTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['menu_ui', 'block', 'test_page_test'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests cache tags presence and invalidation of the Menu entity.
*
* Tests the following cache tags:
* - "menu:<menu ID>"
*/
public function testMenuBlock(): void {
$url = Url::fromRoute('test_page_test.test_page');
// Create a Llama menu, add a link to it and place the corresponding block.
$menu = Menu::create([
'id' => 'llama',
'label' => 'Llama',
'description' => 'Description text',
]);
$menu->save();
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
// Move a link into the new menu.
$menu_link = $menu_link_manager->updateDefinition('test_page_test.test_page', ['menu_name' => 'llama', 'parent' => '']);
$block = $this->drupalPlaceBlock('system_menu_block:llama', ['label' => 'Llama', 'provider' => 'system', 'region' => 'footer']);
// Prime the page cache.
$this->verifyPageCache($url, 'MISS');
// Verify a cache hit, but also the presence of the correct cache tags.
$expected_tags = [
'http_response',
'rendered',
'block_view',
'config:block_list',
'config:block.block.' . $block->id(),
'config:system.menu.llama',
// The cache contexts associated with the (in)accessible menu links are
// bubbled.
'config:user.role.anonymous',
];
$this->verifyPageCache($url, 'HIT', $expected_tags);
// Verify that after modifying the menu, there is a cache miss.
$menu->set('label', 'Awesome llama');
$menu->save();
$this->verifyPageCache($url, 'MISS');
// Verify a cache hit.
$this->verifyPageCache($url, 'HIT');
// Verify that after modifying the menu link weight, there is a cache miss.
$menu_link_manager->updateDefinition('test_page_test.test_page', ['weight' => -10]);
$this->verifyPageCache($url, 'MISS');
// Verify a cache hit.
$this->verifyPageCache($url, 'HIT');
// Verify that after adding a menu link, there is a cache miss.
$menu_link_2 = MenuLinkContent::create([
'id' => '',
'parent' => '',
'title' => 'Alpaca',
'menu_name' => 'llama',
'link' => [
['uri' => 'internal:/'],
],
'bundle' => 'menu_name',
]);
$menu_link_2->save();
$this->verifyPageCache($url, 'MISS');
// Verify a cache hit.
$this->verifyPageCache($url, 'HIT');
// Verify that after resetting the first menu link, there is a cache miss.
$this->assertTrue($menu_link->isResettable(), 'First link can be reset');
$menu_link = $menu_link_manager->resetLink($menu_link->getPluginId());
$this->verifyPageCache($url, 'MISS');
// Verify a cache hit.
$this->verifyPageCache($url, 'HIT', $expected_tags);
// Verify that after deleting the menu, there is a cache miss.
$menu->delete();
$this->verifyPageCache($url, 'MISS');
// Verify a cache hit.
$this->verifyPageCache($url, 'HIT', ['config:block_list', 'config:user.role.anonymous', 'http_response', 'rendered']);
}
}

View File

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Reorder menu items.
*
* @group menu_ui
*/
class MenuLinkReorderTest extends BrowserTestBase {
/**
* An administrator user.
*
* @var \Drupal\user\UserInterface
*/
protected $administrator;
/**
* {@inheritdoc}
*/
protected static $modules = ['menu_ui', 'test_page_test', 'node', 'block'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests creating, editing, deleting menu links via node form widget.
*/
public function testDefaultMenuLinkReorder(): void {
// Add the main menu block.
$this->drupalPlaceBlock('system_menu_block:main');
// Assert that the Home link is available.
$this->drupalGet('test-page');
$this->assertSession()->linkExists('Home');
// The administrator user that can re-order menu links.
$this->administrator = $this->drupalCreateUser([
'administer site configuration',
'access administration pages',
'administer menu',
]);
$this->drupalLogin($this->administrator);
// Change the weight of the link to a non default value.
$edit = [
'links[menu_plugin_id:test_page_test.front_page][weight]' => -10,
];
$this->drupalGet('admin/structure/menu/manage/main');
$this->submitForm($edit, 'Save');
// The link is still there.
$this->drupalGet('test-page');
$this->assertSession()->linkExists('Home');
// Clear all caches.
$this->drupalGet('admin/config/development/performance');
$this->submitForm([], 'Clear all caches');
// Clearing all caches should not affect the state of the menu link.
$this->drupalGet('test-page');
$this->assertSession()->linkExists('Home');
}
}

View File

@ -0,0 +1,247 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Functional;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait;
/**
* Tests Menu UI and Content Moderation integration.
*
* @group menu_ui
*/
class MenuUiContentModerationTest extends BrowserTestBase {
use ContentModerationTestTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'block',
'content_moderation',
'node',
'menu_ui',
'test_page_test',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalPlaceBlock('system_menu_block:main');
// Create a 'page' content type.
$this->drupalCreateContentType([
'type' => 'page',
'name' => 'Basic page',
'display_submitted' => FALSE,
]);
$workflow = $this->createEditorialWorkflow();
$workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'page');
$workflow->save();
}
/**
* Tests that node drafts can not modify the menu settings.
*/
public function testMenuUiWithPendingRevisions(): void {
$editor = $this->drupalCreateUser([
'administer nodes',
'administer menu',
'create page content',
'edit any page content',
'use editorial transition create_new_draft',
'use editorial transition publish',
'view latest version',
'view any unpublished content',
]);
$this->drupalLogin($editor);
// Create a node.
$node = $this->drupalCreateNode();
// Publish the node with no changes.
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm([], 'Save');
$this->assertSession()->pageTextContains("Page {$node->label()} has been updated.");
// Create a pending revision with no changes.
$edit = ['moderation_state[0][state]' => 'draft'];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains("Page {$node->label()} has been updated.");
// Add a menu link and save a new default (published) revision.
$edit = [
'menu[enabled]' => 1,
'menu[title]' => 'Test menu link',
'moderation_state[0][state]' => 'published',
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertSession()->linkExists('Test menu link');
// Try to change the menu link weight and save a new non-default (draft)
// revision.
$edit = [
'menu[weight]' => 1,
'moderation_state[0][state]' => 'draft',
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
// Check that the menu settings were not applied.
$this->assertSession()->pageTextContains('You can only change the menu link weight for the published version of this content.');
// Try to change the menu link parent and save a new non-default (draft)
// revision.
$edit = [
'menu[menu_parent]' => 'main:test_page_test.front_page',
'moderation_state[0][state]' => 'draft',
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
// Check that the menu settings were not applied.
$this->assertSession()->pageTextContains('You can only change the parent menu link for the published version of this content.');
// Try to delete the menu link and save a new non-default (draft) revision.
$edit = [
'menu[enabled]' => 0,
'moderation_state[0][state]' => 'draft',
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
// Check that the menu settings were not applied.
$this->assertSession()->pageTextContains('You can only remove the menu link in the published version of this content.');
$this->assertSession()->linkExists('Test menu link');
// Try to change the menu link title and description and save a new
// non-default (draft) revision.
$edit = [
'menu[title]' => 'Test menu link draft',
'menu[description]' => 'Test menu link description',
'moderation_state[0][state]' => 'draft',
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains("Page {$node->label()} has been updated.");
// Ensure the content was not immediately published.
$this->assertSession()->linkExists('Test menu link');
// Publish the node and ensure the new link text was published.
$edit = [
'moderation_state[0][state]' => 'published',
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertSession()->linkExists('Test menu link draft');
// Try to save a new non-default (draft) revision without any changes and
// check that the error message is not shown.
$edit = ['moderation_state[0][state]' => 'draft'];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
// Create a node.
$node = $this->drupalCreateNode();
// Publish the node with no changes.
$edit = ['moderation_state[0][state]' => 'published'];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains("Page {$node->label()} has been updated.");
// Add a menu link and save and create a new non-default (draft) revision
// and ensure it's not immediately published.
$edit = [
'menu[enabled]' => 1,
'menu[title]' => 'Second test menu link',
'moderation_state[0][state]' => 'draft',
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains("Page {$node->label()} has been updated.");
// The link is created to the latest page, which the editor is allowed
// see, but an anonymous visitor not.
$this->assertSession()->linkExists('Second test menu link');
$this->drupalLogout();
$this->assertSession()->linkNotExists('Second test menu link');
$this->drupalLogin($editor);
// Publish the content and ensure the new menu link shows up.
$edit = [
'moderation_state[0][state]' => 'published',
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains("Page {$node->label()} has been updated.");
$this->assertSession()->linkExists('Second test menu link');
}
/**
* Tests that unpublished content can be selected through the menu UI.
*/
public function testMenuUiWithUnpublishedContent(): void {
$editor_with_unpublished_content_access = $this->drupalCreateUser([
'administer nodes',
'administer menu',
'create page content',
'use editorial transition create_new_draft',
'view any unpublished content',
]);
$this->drupalLogin($editor_with_unpublished_content_access);
// Create a node.
$node_title = $this->randomMachineName();
$edit = [
'title[0][value]' => $node_title,
'menu[enabled]' => 1,
'menu[title]' => $node_title,
'moderation_state[0][state]' => 'draft',
];
$this->drupalGet('node/add/page');
$this->submitForm($edit, 'Save');
// Assert that the unpublished node can be selected as a parent menu link
// for users with access to the node.
$node = $this->drupalGetNodeByTitle($node_title);
$this->assertTrue($node->access('view', $editor_with_unpublished_content_access));
$this->assertEquals($edit['title[0][value]'], $node->getTitle());
$this->drupalGet('node/add/page');
$link_id = menu_ui_get_menu_link_defaults($node)['entity_id'];
/** @var \Drupal\menu_link_content\Entity\MenuLinkContent $link */
$link = MenuLinkContent::load($link_id);
$this->assertSession()->optionExists('edit-menu-menu-parent', 'main:' . $link->getPluginId());
// Assert that the unpublished node cannot be selected as a parent menu link
// for users without access to the node.
$editor_without_unpublished_content_access = $this->drupalCreateUser([
'administer nodes',
'administer menu',
'create page content',
'use editorial transition create_new_draft',
]);
$this->drupalLogin($editor_without_unpublished_content_access);
$this->assertFalse($node->access('view', $editor_without_unpublished_content_access));
$this->drupalGet('node/add/page');
$this->assertSession()->optionNotExists('edit-menu-menu-parent', 'main:' . $link->getPluginId());
}
}

View File

@ -0,0 +1,175 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Functional;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\language\Traits\LanguageTestTrait;
/**
* Tests Menu UI and Content Translation integration for content entities.
*
* @group menu_ui
*/
class MenuUiContentTranslationTest extends BrowserTestBase {
use LanguageTestTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected static $modules = [
'block',
'language',
'content_translation',
'menu_ui',
'node',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Place menu block and local tasks block.
$this->drupalPlaceBlock('system_menu_block:main');
$this->drupalPlaceBlock('local_tasks_block');
// Create a 'page' content type.
$this->drupalCreateContentType([
'type' => 'page',
'name' => 'Basic page',
'display_submitted' => FALSE,
]);
// Add a second language.
static::createLanguageFromLangcode('de');
// Create an account and login.
$user = $this->drupalCreateUser([
'administer site configuration',
'administer nodes',
'create page content',
'edit any page content',
'delete any page content',
'administer content translation',
'translate any entity',
'create content translations',
'administer languages',
'administer content types',
'administer menu',
]);
$this->drupalLogin($user);
// Enable translation for page nodes and menu link content.
static::enableBundleTranslation('node', 'page');
static::enableBundleTranslation('menu_link_content', 'menu_link_content');
}
/**
* Gets a content entity object by title.
*
* @param string $entity_type_id
* Id of content entity type of content entity to load.
* @param string $title
* Title of content entity to load.
*
* @return \Drupal\Core\Entity\ContentEntityInterface
* First found content entity with given title.
*/
protected function getContentEntityByTitle($entity_type_id, $title) {
$entity_type_manager = $this->container->get('entity_type.manager');
$storage = $entity_type_manager->getStorage($entity_type_id);
$storage->resetCache();
$entities = $storage->loadByProperties([
'title' => $title,
]);
return reset($entities);
}
/**
* Provides test data sets for testChangeContentToPseudoLanguage().
*
* @return array
* Data sets to test keyed by data set label.
*/
public static function provideChangeContentToPseudoLanguageData() {
return [
'und' => ['langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED],
'zxx' => ['langcode' => LanguageInterface::LANGCODE_NOT_APPLICABLE],
];
}
/**
* Tests changing content with menu link from language to pseudo language.
*
* @param string $langcode
* Language code of pseudo-language to change content language to.
* Either \Drupal\Core\LanguageInterface::LANGCODE_NOT_SPECIFIED or
* \Drupal\Core\LanguageInterface::LANGCODE_NOT_APPLICABLE.
*
* @dataProvider provideChangeContentToPseudoLanguageData
*/
public function testChangeContentToPseudoLanguage($langcode): void {
$node_title = 'Test node';
$menu_link_title_en = 'Test menu link EN';
$menu_link_title_pseudo = 'Test menu link PSEUDO';
// Create a page node in English.
$this->drupalGet('node/add/page');
$this->assertSession()->statusCodeEquals(200);
$edit = [
'title[0][value]' => $node_title,
'menu[enabled]' => 1,
'menu[title]' => $menu_link_title_en,
];
$this->submitForm($edit, 'Save');
$this->assertSession()->statusCodeEquals(200);
// Assert that node exists and node language is English.
$node = $this->getContentEntityByTitle('node', $node_title);
$this->assertTrue(is_object($node));
$this->assertTrue($node->language()->getId() == 'en');
// Assert that menu link exists and menu link language is English.
$menu_link = $this->getContentEntityByTitle('menu_link_content', $menu_link_title_en);
$this->assertTrue(is_object($menu_link));
$this->assertTrue($menu_link->language()->getId() == 'en');
$this->assertTrue($menu_link->hasTranslation('en'));
// Assert that menu link is visible with initial title.
$this->assertSession()->linkExists($menu_link_title_en);
// Change language of page node and title of its menu link.
$this->clickLink('Edit');
$edit = [
'langcode[0][value]' => $langcode,
'menu[title]' => $menu_link_title_pseudo,
];
$this->submitForm($edit, 'Save');
$this->assertSession()->statusCodeEquals(200);
// Assert that node exists and node language is target language.
$node = $this->getContentEntityByTitle('node', $node_title);
$this->assertTrue(is_object($node));
$this->assertTrue($node->language()->getId() == $langcode);
// Assert that menu link exists and menu link language is target language.
$menu_link = $this->getContentEntityByTitle('menu_link_content', $menu_link_title_pseudo);
$this->assertTrue(is_object($menu_link));
$this->assertTrue($menu_link->language()->getId() == $langcode);
$this->assertFalse($menu_link->hasTranslation('en'));
// Assert that menu link is visible with updated title.
$this->assertSession()->linkExists($menu_link_title_pseudo);
}
}

View File

@ -0,0 +1,155 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\language\Entity\ContentLanguageSettings;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\menu_ui\Traits\MenuUiTrait;
/**
* Tests for menu_ui language settings.
*
* Create menu and menu links in non-English language, and edit language
* settings.
*
* @group menu_ui
*/
class MenuUiLanguageTest extends BrowserTestBase {
use MenuUiTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'language',
'menu_link_content',
'menu_ui',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser([
'access administration pages',
'administer menu',
]));
// Add some custom languages.
foreach (['aa', 'bb', 'cc', 'cs'] as $language_code) {
ConfigurableLanguage::create([
'id' => $language_code,
'label' => $this->randomMachineName(),
])->save();
}
}
/**
* Tests menu language settings and the defaults for menu link items.
*/
public function testMenuLanguage(): void {
// Create a test menu to test the various language-related settings.
// Machine name has to be lowercase.
$menu_name = $this->randomMachineName(16);
$label = $this->randomString();
$edit = [
'id' => $menu_name,
'description' => '',
'label' => $label,
'langcode' => 'aa',
];
$this->drupalGet('admin/structure/menu/add');
$this->submitForm($edit, 'Save');
ContentLanguageSettings::loadByEntityTypeBundle('menu_link_content', 'menu_link_content')
->setDefaultLangcode('bb')
->setLanguageAlterable(TRUE)
->save();
// Check menu language.
$this->assertTrue($this->assertSession()->optionExists('edit-langcode', $edit['langcode'])->isSelected());
// Test menu link language.
$link_path = '/';
// Add a menu link.
$link_title = $this->randomString();
$edit = [
'title[0][value]' => $link_title,
'link[0][uri]' => $link_path,
];
$this->drupalGet("admin/structure/menu/manage/{$menu_name}/add");
$this->submitForm($edit, 'Save');
// Check the link was added with the correct menu link default language.
$menu_links = \Drupal::entityTypeManager()->getStorage('menu_link_content')->loadByProperties(['title' => $link_title]);
$menu_link = reset($menu_links);
$this->assertMenuLink([
'menu_name' => $menu_name,
'route_name' => '<front>',
'langcode' => 'bb',
], $menu_link->getPluginId());
// Edit menu link default, changing it to cc.
ContentLanguageSettings::loadByEntityTypeBundle('menu_link_content', 'menu_link_content')
->setDefaultLangcode('cc')
->setLanguageAlterable(TRUE)
->save();
// Add a menu link.
$link_title = $this->randomString();
$edit = [
'title[0][value]' => $link_title,
'link[0][uri]' => $link_path,
];
$this->drupalGet("admin/structure/menu/manage/{$menu_name}/add");
$this->submitForm($edit, 'Save');
// Check the link was added with the correct new menu link default language.
$menu_links = \Drupal::entityTypeManager()->getStorage('menu_link_content')->loadByProperties(['title' => $link_title]);
$menu_link = reset($menu_links);
$this->assertMenuLink([
'menu_name' => $menu_name,
'route_name' => '<front>',
'langcode' => 'cc',
], $menu_link->getPluginId());
// Now change the language of the new link to 'bb'.
$edit = [
'langcode[0][value]' => 'bb',
];
$this->drupalGet('admin/structure/menu/item/' . $menu_link->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->assertMenuLink([
'menu_name' => $menu_name,
'route_name' => '<front>',
'langcode' => 'bb',
], $menu_link->getPluginId());
// Saving menu link items ends up on the edit menu page. To check the menu
// link has the correct language default on edit, go to the menu link edit
// page first.
$this->drupalGet('admin/structure/menu/item/' . $menu_link->id() . '/edit');
// Check that the language selector has the correct default value.
$this->assertTrue($this->assertSession()->optionExists('edit-langcode-0-value', 'bb')->isSelected());
// Edit menu to hide the language select on menu link item add.
ContentLanguageSettings::loadByEntityTypeBundle('menu_link_content', 'menu_link_content')
->setDefaultLangcode('cc')
->setLanguageAlterable(FALSE)
->save();
// Check that the language selector is not available on menu link add page.
$this->drupalGet("admin/structure/menu/manage/$menu_name/add");
$this->assertSession()->fieldNotExists('edit-langcode-0-value');
}
}

View File

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Functional;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\node\Entity\Node;
use Drupal\Tests\BrowserTestBase;
/**
* Edit a node when you don't have permission to add or edit menu links.
*
* @group menu_ui
*/
class MenuUiNodeAccessTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'menu_ui',
'test_page_test',
'node',
'menu_link_access_test',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
}
/**
* Tests menu link create access is enforced.
*/
public function testMenuLinkCreateAccess(): void {
$this->drupalLogin($this->drupalCreateUser([
'administer menu',
'edit any page content',
]));
$node = Node::create([
'type' => 'page',
'title' => $this->randomMachineName(),
'uid' => $this->rootUser->id(),
'status' => 1,
]);
$node->save();
$this->drupalGet('node/' . $node->id() . '/edit');
$this->assertSession()->elementNotExists('css', 'input[name="menu[title]"]');
}
/**
* Tests menu link edit/delete access is enforced.
*/
public function testMenuLinkEditAccess(): void {
$this->drupalLogin($this->drupalCreateUser([
'administer menu',
'edit any page content',
]));
$mainLinkTitle = $this->randomMachineName();
$node = Node::create([
'type' => 'page',
'title' => $this->randomMachineName(),
'uid' => $this->rootUser->id(),
'status' => 1,
]);
$node->save();
MenuLinkContent::create([
'link' => [['uri' => 'entity:node/' . $node->id()]],
'title' => $mainLinkTitle,
'menu_name' => 'main',
])->save();
$this->drupalGet('node/' . $node->id() . '/edit');
$this->assertSession()->elementNotExists('css', 'input[name="menu[title]"]');
}
}

View File

@ -0,0 +1,469 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\system\Entity\Menu;
use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\content_translation\Traits\ContentTranslationTestTrait;
/**
* Add, edit, and delete a node with menu link.
*
* @group menu_ui
*/
class MenuUiNodeTest extends BrowserTestBase {
use ContentTranslationTestTrait;
/**
* An editor user.
*
* @var \Drupal\user\UserInterface
*/
protected $editor;
/**
* {@inheritdoc}
*/
protected static $modules = [
'menu_ui',
'test_page_test',
'node',
'block',
'locale',
'language',
'content_translation',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->drupalPlaceBlock('system_menu_block:main');
$this->drupalPlaceBlock('page_title_block');
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
$this->editor = $this->drupalCreateUser([
'access administration pages',
'administer content types',
'administer menu',
'create page content',
'edit any page content',
'delete any page content',
'create content translations',
'update content translations',
'delete content translations',
'translate any entity',
]);
$this->drupalLogin($this->editor);
}
/**
* Tests creating, editing, deleting menu links via node form widget.
*/
public function testMenuNodeFormWidget(): void {
// Verify that cacheability metadata is bubbled from the menu link tree
// access checking that is performed when determining the "default parent
// item" options in menu_ui_form_node_type_form_alter(). The "log out" link
// adds the "user.roles:authenticated" cache context.
$this->drupalGet('admin/structure/types/manage/page');
$this->assertSession()->responseHeaderContains('X-Drupal-Cache-Contexts', 'user.roles:authenticated');
// Assert the description of "Available menus" checkboxes field.
$this->assertSession()->pageTextContains('Content of this type can be placed in the selected menus.');
// Verify that the menu link title has the correct maxlength.
$title_max_length = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions('menu_link_content')['title']->getSetting('max_length');
$this->drupalGet('node/add/page');
$this->assertSession()->responseMatches('/<input .* id="edit-menu-title" .* maxlength="' . $title_max_length . '" .* \/>/');
// Verify that the menu link description has the correct maxlength.
$description_max_length = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions('menu_link_content')['description']->getSetting('max_length');
$this->drupalGet('node/add/page');
$this->assertSession()->responseMatches('/<input .* id="edit-menu-description" .* maxlength="' . $description_max_length . '" .* \/>/');
// Disable the default main menu, so that no menus are enabled.
$edit = [
'menu_options[main]' => FALSE,
];
$this->drupalGet('admin/structure/types/manage/page');
$this->submitForm($edit, 'Save');
// Verify that no menu settings are displayed and nodes can be created.
$this->drupalGet('node/add/page');
$this->assertSession()->pageTextContains('Create Basic page');
$this->assertSession()->pageTextNotContains('Menu settings');
$node_title = $this->randomMachineName();
$edit = [
'title[0][value]' => $node_title,
'body[0][value]' => $this->randomString(),
];
$this->submitForm($edit, 'Save');
$node = $this->drupalGetNodeByTitle($node_title);
$this->assertEquals($edit['title[0][value]'], $node->getTitle());
// Test that we cannot set a menu item from a menu that is not set as
// available.
$edit = [
'menu_options[tools]' => 1,
'menu_parent' => 'main:',
];
$this->drupalGet('admin/structure/types/manage/page');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('The selected menu link is not under one of the selected menus.');
$this->assertSession()->pageTextNotContains("The content type Basic page has been updated.");
// Enable Tools menu as available menu.
$edit = [
'menu_options[main]' => 1,
'menu_options[tools]' => 1,
'menu_parent' => 'main:',
];
$this->drupalGet('admin/structure/types/manage/page');
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains("The content type Basic page has been updated.");
// Test that we can preview a node that will create a menu item.
$edit = [
'title[0][value]' => $node_title,
'menu[enabled]' => 1,
'menu[title]' => 'Test preview',
];
$this->drupalGet('node/add/page');
$this->submitForm($edit, 'Preview');
// Create a node.
$node_title = $this->randomMachineName();
$edit = [
'title[0][value]' => $node_title,
'body[0][value]' => $this->randomString(),
];
$this->drupalGet('node/add/page');
$this->submitForm($edit, 'Save');
$node = $this->drupalGetNodeByTitle($node_title);
// Assert that there is no link for the node.
$this->drupalGet('test-page');
$this->assertSession()->linkNotExists($node_title);
// Edit the node, enable the menu link setting, but skip the link title.
$edit = [
'menu[enabled]' => 1,
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
// Assert that there is a link for the node.
$this->drupalGet('test-page');
$this->assertSession()->linkExists($node_title);
// Make sure the menu links only appear when the node is published.
// These buttons just appear for 'administer nodes' users.
$admin_user = $this->drupalCreateUser([
'access administration pages',
'administer content types',
'administer nodes',
'administer menu',
'create page content',
'edit any page content',
]);
$this->drupalLogin($admin_user);
// Assert that the link does not exist if unpublished.
$edit = [
'menu[enabled]' => 1,
'menu[title]' => $node_title,
'status[value]' => FALSE,
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->drupalGet('test-page');
$this->assertSession()->linkNotExists($node_title, 'Found no menu link with the node unpublished');
// Assert that the link exists if published.
$edit = [
'status[value]' => TRUE,
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$this->drupalGet('test-page');
$this->assertSession()->linkExists($node_title, 0, 'Found a menu link with the node published');
// Log back in as normal user.
$this->drupalLogin($this->editor);
// Edit the node and create a menu link.
$edit = [
'menu[enabled]' => 1,
'menu[title]' => $node_title,
'menu[weight]' => 17,
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
// Assert that the link exists.
$this->drupalGet('test-page');
$this->assertSession()->linkExists($node_title);
// Check if menu weight is 17.
$this->drupalGet('node/' . $node->id() . '/edit');
$this->assertSession()->fieldValueEquals('edit-menu-weight', 17);
// Verify that the menu link title field has correct maxlength in node edit
// form.
$this->assertSession()->responseMatches('/<input .* id="edit-menu-title" .* maxlength="' . $title_max_length . '" .* \/>/');
// Verify that the menu link description field has correct maxlength in
// node add form.
$this->assertSession()->responseMatches('/<input .* id="edit-menu-description" .* maxlength="' . $description_max_length . '" .* \/>/');
// Disable the menu link, then edit the node--the link should stay disabled.
$link_id = menu_ui_get_menu_link_defaults($node)['entity_id'];
/** @var \Drupal\menu_link_content\Entity\MenuLinkContent $link */
$link = MenuLinkContent::load($link_id);
$link->set('enabled', FALSE);
$link->save();
$this->drupalGet($node->toUrl('edit-form'));
$this->submitForm($edit, 'Save');
$link = MenuLinkContent::load($link_id);
$this->assertFalse($link->isEnabled(), 'Saving a node with a disabled menu link keeps the menu link disabled.');
// Edit the node and remove the menu link.
$edit = [
'menu[enabled]' => FALSE,
];
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
// Assert that there is no link for the node.
$this->drupalGet('test-page');
$this->assertSession()->linkNotExists($node_title);
// Add a menu link to the Administration menu.
$item = MenuLinkContent::create([
'link' => [['uri' => 'entity:node/' . $node->id()]],
'title' => $this->randomMachineName(16),
'menu_name' => 'admin',
]);
$item->save();
// Assert that disabled Administration menu is not shown on the
// node/$nid/edit page.
$this->drupalGet('node/' . $node->id() . '/edit');
$this->assertSession()->pageTextContains('Provide a menu link');
// Assert that the link is still in the Administration menu after save.
$this->drupalGet('node/' . $node->id() . '/edit');
$this->submitForm($edit, 'Save');
$link = MenuLinkContent::load($item->id());
$this->assertInstanceOf(MenuLinkContent::class, $link);
// Move the menu link back to the Tools menu.
$item->menu_name->value = 'tools';
$item->save();
// Create a second node.
$child_node = $this->drupalCreateNode(['type' => 'article']);
// Assign a menu link to the second node, being a child of the first one.
$child_item = MenuLinkContent::create([
'link' => [['uri' => 'entity:node/' . $child_node->id()]],
'title' => $this->randomMachineName(16),
'parent' => $item->getPluginId(),
'menu_name' => $item->getMenuName(),
]);
$child_item->save();
// Edit the first node.
$this->drupalGet('node/' . $node->id() . '/edit');
// Assert that it is not possible to set the parent of the first node to
// itself or the second node.
$this->assertSession()->optionNotExists('edit-menu-menu-parent', 'tools:' . $item->getPluginId());
$this->assertSession()->optionNotExists('edit-menu-menu-parent', 'tools:' . $child_item->getPluginId());
// Assert that disallowed Administration menu is not available in options.
$this->assertSession()->optionNotExists('edit-menu-menu-parent', 'admin:');
}
/**
* Testing correct loading and saving of menu links via node form widget in a multilingual environment.
*/
public function testMultilingualMenuNodeFormWidget(): void {
// Setup languages.
$langcodes = ['de'];
foreach ($langcodes as $langcode) {
static::createLanguageFromLangcode($langcode);
}
array_unshift($langcodes, \Drupal::languageManager()->getDefaultLanguage()->getId());
$config = \Drupal::service('config.factory')->getEditable('language.negotiation');
// Ensure path prefix is used to determine the language.
$config->set('url.source', 'path_prefix');
// Ensure that there's a path prefix set for english as well.
$config->set('url.prefixes.' . $langcodes[0], $langcodes[0]);
$config->save();
$languages = [];
foreach ($langcodes as $langcode) {
$languages[$langcode] = ConfigurableLanguage::load($langcode);
}
// Enable translation for pages and menu link content..
$this->enableContentTranslation('node', 'page');
$this->enableContentTranslation('menu_link_content', 'menu_link_content');
$this->rebuildContainer();
// Create a node.
$node_title = $this->randomMachineName(8);
$node = Node::create([
'type' => 'page',
'title' => $node_title,
'body' => $this->randomMachineName(16),
'uid' => $this->editor->id(),
'status' => 1,
'langcode' => $langcodes[0],
]);
$node->save();
// Create translation.
$translated_node_title = $this->randomMachineName(8);
$node->addTranslation($langcodes[1], ['title' => $translated_node_title, 'body' => $this->randomMachineName(16), 'status' => 1]);
$node->save();
// Edit the node and create a menu link.
$edit = [
'menu[enabled]' => 1,
'menu[title]' => $node_title,
'menu[weight]' => 17,
];
$options = ['language' => $languages[$langcodes[0]]];
$url = $node->toUrl('edit-form', $options);
$this->drupalGet($url);
$this->submitForm($edit, 'Save (this translation)');
// Edit the node in a different language and translate the menu link.
$edit = [
'menu[enabled]' => 1,
'menu[title]' => $translated_node_title,
'menu[weight]' => 17,
];
$options = ['language' => $languages[$langcodes[1]]];
$url = $node->toUrl('edit-form', $options);
$this->drupalGet($url);
$this->submitForm($edit, 'Save (this translation)');
// Assert that the original link exists in the frontend.
$this->drupalGet('node/' . $node->id(), ['language' => $languages[$langcodes[0]]]);
$this->assertSession()->linkExists($node_title);
// Assert that the translated link exists in the frontend.
$this->drupalGet('node/' . $node->id(), ['language' => $languages[$langcodes[1]]]);
$this->assertSession()->linkExists($translated_node_title);
// Revisit the edit page in original language, check the loaded menu item
// title and save.
$options = ['language' => $languages[$langcodes[0]]];
$url = $node->toUrl('edit-form', $options);
$this->drupalGet($url);
$this->assertSession()->fieldValueEquals('edit-menu-title', $node_title);
$this->submitForm([], 'Save (this translation)');
// Revisit the edit page of the translation and check the loaded menu item
// title.
$options = ['language' => $languages[$langcodes[1]]];
$url = $node->toUrl('edit-form', $options);
$this->drupalGet($url);
$this->assertSession()->fieldValueEquals('edit-menu-title', $translated_node_title);
}
/**
* Tests creating menu links via node form widget for nodes with grants.
*/
public function testMenuNodeWithGrantsFormWidget(): void {
\Drupal::service('module_installer')->install(['node_access_test']);
node_access_rebuild();
$this->assertTrue(\Drupal::moduleHandler()->hasImplementations('node_grants'));
$admin_user = $this->drupalCreateUser([
'access administration pages',
'administer content types',
'administer nodes',
'administer menu',
'create page content',
'edit any page content',
]);
$this->drupalLogin($admin_user);
$node_title = $this->randomMachineName();
$edit = [
'title[0][value]' => $node_title,
'menu[enabled]' => 1,
'menu[title]' => $node_title,
'status[value]' => 0,
];
$this->drupalGet('node/add/page');
$this->submitForm($edit, 'Save');
$node = $this->drupalGetNodeByTitle($node_title);
$this->assertTrue($node->access('view', $admin_user));
$this->drupalGet('node/add/page');
$link_id = menu_ui_get_menu_link_defaults($node)['entity_id'];
/** @var \Drupal\menu_link_content\Entity\MenuLinkContent $link */
$link = MenuLinkContent::load($link_id);
$this->assertSession()->optionExists('edit-menu-menu-parent', 'main:' . $link->getPluginId());
// Assert that the unpublished node cannot be selected as a parent menu link
// for users without access to the node.
$admin_user_without_content_access = $this->drupalCreateUser([
'access administration pages',
'administer content types',
'administer nodes',
'administer menu',
'create page content',
'edit any page content',
]);
$this->drupalLogin($admin_user_without_content_access);
$this->assertFalse($node->access('view', $admin_user_without_content_access));
$this->drupalGet('node/add/page');
$this->assertSession()->optionNotExists('edit-menu-menu-parent', 'main:' . $link->getPluginId());
}
/**
* Tests main menu links are prioritized when editing nodes.
*
* @see menu_ui_get_menu_link_defaults()
*/
public function testMainMenuIsPrioritized(): void {
$this->drupalLogin($this->drupalCreateUser([
'administer menu',
'edit any page content',
]));
$menu_name = $this->randomMachineName();
$mainLinkTitle = $this->randomMachineName();
$nonMainLinkTitle = $this->randomMachineName();
Menu::create(['id' => $menu_name, 'label' => $menu_name])->save();
$nodeType = NodeType::load('page');
$nodeType->setThirdPartySetting('menu_ui', 'available_menus', [$menu_name, 'main'])->save();
$node = Node::create([
'type' => 'page',
'title' => $this->randomMachineName(),
'uid' => $this->rootUser->id(),
'status' => 1,
]);
$node->save();
MenuLinkContent::create([
'link' => [['uri' => 'entity:node/' . $node->id()]],
'title' => $nonMainLinkTitle,
'menu_name' => $menu_name,
])->save();
MenuLinkContent::create([
'link' => [['uri' => 'entity:node/' . $node->id()]],
'title' => $mainLinkTitle,
'menu_name' => 'main',
])->save();
$this->drupalGet('node/' . $node->id() . '/edit');
$element = $this->assertSession()->elementExists('css', 'input[name="menu[title]"]');
$this->assertEquals($mainLinkTitle, $element->getValue());
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Functional;
use Drupal\Tests\BrowserTestBase;
use Drupal\system\Entity\Menu;
/**
* Tests that uninstalling menu does not remove custom menus.
*
* @group menu_ui
*/
class MenuUninstallTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['menu_ui'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests Menu uninstall.
*/
public function testMenuUninstall(): void {
\Drupal::service('module_installer')->uninstall(['menu_ui']);
\Drupal::entityTypeManager()->getStorage('menu')->resetCache(['admin']);
$this->assertNotEmpty(Menu::load('admin'), 'The \'admin\' menu still exists after uninstalling Menu UI module.');
}
}

View File

@ -0,0 +1,165 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\FunctionalJavascript;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\system\Entity\Menu;
use Drupal\system\MenuStorage;
use Drupal\Tests\contextual\FunctionalJavascript\ContextualLinkClickTrait;
use Drupal\Tests\menu_ui\Traits\MenuUiTrait;
/**
* Tests custom menu and menu links operations using the UI.
*
* @group menu_ui
*/
class MenuUiJavascriptTest extends WebDriverTestBase {
use ContextualLinkClickTrait;
use MenuUiTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'block',
'contextual',
'menu_link_content',
'menu_ui',
'test_page_test',
];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests the contextual links on a menu block.
*/
public function testBlockContextualLinks(): void {
$this->drupalLogin($this->drupalCreateUser([
'administer menu',
'access contextual links',
'administer blocks',
]));
$menu = $this->addCustomMenu();
$block = $this->drupalPlaceBlock('system_menu_block:' . $menu->id(), [
'label' => 'Custom menu',
'provider' => 'system',
]);
$this->addMenuLink('', '/', $menu->id());
$this->drupalGet('test-page');
// Click on 'Configure block' contextual link.
$this->clickContextualLink("#block-{$block->id()}", 'Configure block');
// Check that we're on block configuration form.
$this->assertNotEmpty($this->getSession()->getPage()->findLink('Remove block'));
$this->drupalGet('test-page');
// Click on 'Edit menu' contextual link.
$this->clickContextualLink("#block-{$block->id()}", 'Edit menu');
// Check that we're on block configuration form.
$this->assertSession()->pageTextContains("Machine name: {$menu->id()}");
}
/**
* Creates a custom menu.
*
* @return \Drupal\system\Entity\Menu
* The custom menu that has been created.
*/
protected function addCustomMenu() {
// Try adding a menu using a menu_name that is too long.
$label = $this->randomMachineName(16);
$menu_id = $this->randomMachineName(MenuStorage::MAX_ID_LENGTH + 1);
$this->drupalGet('admin/structure/menu/add');
$page = $this->getSession()->getPage();
// Type the label to activate the machine name field and the edit button.
$page->fillField('Title', $label);
// Wait for the machine name widget to be activated.
$this->assertSession()->waitForElementVisible('css', 'button[type=button].link:contains(Edit)');
// Activate the machine name text field.
$page->pressButton('Edit');
// Try to fill a text longer than the allowed limit.
$page->fillField('Menu name', $menu_id);
$page->pressButton('Save');
// Check that the menu was saved with the ID truncated to the max length.
$menu = Menu::load(substr($menu_id, 0, MenuStorage::MAX_ID_LENGTH));
$this->assertEquals($label, $menu->label());
// Check that the menu was added.
$this->drupalGet('admin/structure/menu');
$this->assertSession()->pageTextContains($label);
// Confirm that the custom menu block is available.
$this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default'));
$this->clickLink('Place block');
// Wait for the modal dialog to be loaded.
$this->assertSession()->waitForElement('css', "div[aria-describedby=drupal-modal]");
// Check that the block is available to be placed.
$this->assertSession()->pageTextContains($label);
return $menu;
}
/**
* Adds a menu link using the UI.
*
* @param string $parent
* Optional parent menu link id.
* @param string $path
* The path to enter on the form. Defaults to the front page.
* @param string $menu_id
* Menu ID. Defaults to 'tools'.
* @param bool $expanded
* Whether or not this menu link is expanded. Setting this to TRUE should
* test whether it works when we do the authenticatedUser tests. Defaults
* to FALSE.
* @param string $weight
* Menu weight. Defaults to 0.
*
* @return \Drupal\menu_link_content\Entity\MenuLinkContent
* A menu link entity.
*/
protected function addMenuLink($parent = '', $path = '/', $menu_id = 'tools', $expanded = FALSE, $weight = '0') {
// View add menu link page.
$this->drupalGet("admin/structure/menu/manage/$menu_id/add");
$title = '!link_' . $this->randomMachineName(16);
$edit = [
'link[0][uri]' => $path,
'title[0][value]' => $title,
'description[0][value]' => '',
'enabled[value]' => 1,
'expanded[value]' => $expanded,
'menu_parent' => $menu_id . ':' . $parent,
'weight[0][value]' => $weight,
];
// Add menu link.
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('The menu link has been saved.');
$storage = $this->container->get('entity_type.manager')->getStorage('menu_link_content');
$menu_links = $storage->loadByProperties(['title' => $title]);
$menu_link = reset($menu_links);
// Check that the stored menu link meeting the expectations.
$this->assertNotNull($menu_link);
$this->assertMenuLink([
'menu_name' => $menu_id,
'children' => [],
'parent' => $parent,
], $menu_link->getPluginId());
return $menu_link;
}
}

View File

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\system\Entity\Menu;
use Drupal\block\Entity\Block;
use Drupal\system\MenuInterface;
use Drupal\Tests\user\Traits\UserCreationTrait;
use Drupal\menu_ui\Hook\MenuUiHooks;
/**
* Tests SystemMenuBlock.
*
* @group menu_ui
*/
class MenuBlockTest extends KernelTestBase {
use UserCreationTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'block',
'menu_ui',
'user',
];
/**
* The menu for testing.
*
* @var \Drupal\system\MenuInterface
*/
protected MenuInterface $menu;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('user');
$this->setUpCurrentUser([], ['administer menu']);
// Add a new custom menu.
$menu_name = 'mock';
$label = $this->randomMachineName(16);
$this->menu = Menu::create([
'id' => $menu_name,
'label' => $label,
'description' => 'Description text',
]);
$this->menu->save();
}
/**
* Tests the editing links for SystemMenuBlock.
*/
public function testOperationLinks(): void {
$block = Block::create([
'plugin' => 'system_menu_block:' . $this->menu->id(),
'region' => 'footer',
'id' => 'machine_name',
'theme' => 'stark',
]);
// Test when user does have "administer menu" permission.
$menuUiEntityOperation = new MenuUiHooks(\Drupal::entityTypeManager());
$this->assertEquals([
'menu-edit' => [
'title' => 'Edit menu',
'url' => $this->menu->toUrl('edit-form'),
'weight' => 50,
],
], $menuUiEntityOperation->entityOperation($block));
$this->setUpCurrentUser();
// Test when user doesn't have "administer menu" permission.
$this->assertEmpty($menuUiEntityOperation->entityOperation($block));
}
}

View File

@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\menu_ui\Hook\MenuUiHooks;
use Drupal\node\Entity\NodeType;
use Drupal\system\Entity\Menu;
/**
* Tests the menu_delete hook.
*
* @group menu_ui
*/
class MenuDeleteTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['node', 'menu_ui', 'system'];
/**
* @covers \Drupal\menu_ui\Hook\MenuUiHooks::menuDelete
* @dataProvider providerMenuDelete
*/
public function testMenuDelete($settings, $expected): void {
$menu = Menu::create([
'id' => 'mock',
'label' => $this->randomMachineName(16),
'description' => 'Description text',
]);
$menu->save();
$content_type = NodeType::create([
'status' => TRUE,
'dependencies' => [
'module' => ['menu_ui'],
],
'third_party_settings' => [
'menu_ui' => $settings,
],
'name' => 'Test type',
'type' => 'test_type',
]);
$content_type->save();
$this->assertEquals($settings['available_menus'], $content_type->getThirdPartySetting('menu_ui', 'available_menus'));
$this->assertEquals($settings['parent'], $content_type->getThirdPartySetting('menu_ui', 'parent'));
$hooks = new MenuUiHooks(\Drupal::entityTypeManager());
$hooks->menuDelete($menu);
$content_type = NodeType::load('test_type');
$this->assertEquals($expected['available_menus'], $content_type->getThirdPartySetting('menu_ui', 'available_menus'));
$this->assertEquals($expected['parent'], $content_type->getThirdPartySetting('menu_ui', 'parent'));
}
/**
* Provides data for testMenuDelete().
*/
public static function providerMenuDelete(): array {
return [
[
['available_menus' => ['mock'], 'parent' => 'mock:'],
['available_menus' => [], 'parent' => ''],
],
[
['available_menus' => ['mock'], 'parent' => 'mock:menu_link_content:e0cd7689-016e-43e4-af8f-7ce82801ab95'],
['available_menus' => [], 'parent' => ''],
],
[
['available_menus' => ['main', 'mock'], 'parent' => 'mock:'],
['available_menus' => ['main'], 'parent' => ''],
],
[
['available_menus' => ['main'], 'parent' => 'main:'],
['available_menus' => ['main'], 'parent' => 'main:'],
],
[
['available_menus' => ['main'], 'parent' => 'main:menu_link_content:e0cd7689-016e-43e4-af8f-7ce82801ab95'],
['available_menus' => ['main'], 'parent' => 'main:menu_link_content:e0cd7689-016e-43e4-af8f-7ce82801ab95'],
],
];
}
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Kernel;
use Drupal\Core\Menu\LocalActionWithDestination;
use Drupal\KernelTests\KernelTestBase;
use Drupal\menu_ui\Plugin\Menu\LocalAction\MenuLinkAdd;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
/**
* Tests \Drupal\menu_ui\Plugin\Menu\LocalAction\MenuLinkAdd deprecation.
*
* @group menu_ui
* @group legacy
*/
class MenuLinkAddTest extends KernelTestBase {
use ContentTypeCreationTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'menu_ui',
'menu_link_add_test',
];
/**
* Tests \Drupal\menu_ui\Plugin\Menu\LocalAction\MenuLinkAdd deprecation.
*/
public function testDeprecation(): void {
/** @var \Drupal\Core\Menu\LocalActionManagerInterface $local_action_manager */
$local_action_manager = $this->container->get('plugin.manager.menu.local_action');
$this->expectDeprecation('Drupal\menu_ui\Plugin\Menu\LocalAction\MenuLinkAdd is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Use \Drupal\Core\Menu\LocalActionWithDestination instead. See https://www.drupal.org/node/3490245');
$instance = $local_action_manager->createInstance('entity.menu.add_link_form_deprecated');
$this->assertInstanceOf(MenuLinkAdd::class, $instance);
}
/**
* Tests deprecated plugin does not trigger deprecation unless used.
*/
public function testNoDeprecation(): void {
/** @var \Drupal\Core\Menu\LocalActionManagerInterface $local_action_manager */
$local_action_manager = $this->container->get('plugin.manager.menu.local_action');
$instance = $local_action_manager->createInstance('entity.menu.add_link_form');
$this->assertInstanceOf(LocalActionWithDestination::class, $instance);
$deprecated_definition = $local_action_manager->getDefinition('entity.menu.add_link_form_deprecated');
$this->assertSame(MenuLinkAdd::class, $deprecated_definition['class']);
}
}

View File

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\node\Entity\NodeType;
use Drupal\system\Entity\Menu;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
/**
* Tests menu settings when creating and editing content types.
*
* @group menu_ui
*/
class MenuUiNodeTypeTest extends KernelTestBase {
use ContentTypeCreationTrait;
/**
* {@inheritdoc}
*/
protected static $modules = [
'field',
'menu_ui',
'node',
'system',
'text',
'user',
];
/**
* Asserts that the available menu names are sorted alphabetically by label.
*
* @param \Drupal\node\Entity\NodeType $node_type
* The node type under test.
*/
private function assertMenuNamesAreSorted(NodeType $node_type): void {
// The available menus should be sorted by label, not machine name.
$expected_options = [
'b' => 'X',
'c' => 'Y',
'a' => 'Z',
];
$form = $this->container->get('entity.form_builder')
->getForm($node_type, $node_type->isNew() ? 'add' : 'edit');
$this->assertSame($expected_options, $form['menu']['menu_options']['#options']);
}
/**
* Tests node type-specific settings for Menu UI.
*/
public function testContentTypeMenuSettings(): void {
$this->installEntitySchema('node');
$this->installConfig(['node']);
Menu::create(['id' => 'a', 'label' => 'Z'])->save();
Menu::create(['id' => 'b', 'label' => 'X'])->save();
Menu::create(['id' => 'c', 'label' => 'Y'])->save();
$this->assertMenuNamesAreSorted(NodeType::create());
$this->assertMenuNamesAreSorted($this->createContentType());
}
}

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Kernel\Migrate;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Tests migration of menu_ui settings.
*
* @group menu_ui
*/
class MigrateMenuSettingsTest extends MigrateDrupal7TestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['menu_ui'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installConfig(['menu_ui']);
$this->executeMigration('menu_settings');
}
/**
* Tests migration of menu_ui settings.
*/
public function testMigration(): void {
$this->assertTrue(\Drupal::config('menu_ui.settings')->get('override_parent_selector'));
}
}

View File

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Drupal\Tests\menu_ui\Traits;
/**
* Provides common methods for Menu UI module tests.
*/
trait MenuUiTrait {
/**
* Asserts that a menu fetched from the database matches an expected one.
*
* @param array $expected_item
* Array containing properties to check.
* @param string $menu_plugin_id
* Menu item id.
*/
protected function assertMenuLink(array $expected_item, $menu_plugin_id) {
// Retrieve the menu link.
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
$menu_link_manager->resetDefinitions();
// Reset the static load cache.
\Drupal::entityTypeManager()->getStorage('menu_link_content')->resetCache();
$definition = $menu_link_manager->getDefinition($menu_plugin_id);
$entity = NULL;
// Pull the path from the menu link content.
if (str_starts_with($menu_plugin_id, 'menu_link_content')) {
[, $uuid] = explode(':', $menu_plugin_id, 2);
/** @var \Drupal\menu_link_content\Entity\MenuLinkContent $entity */
$entity = \Drupal::service('entity.repository')
->loadEntityByUuid('menu_link_content', $uuid);
}
if (isset($expected_item['children'])) {
$child_ids = array_values($menu_link_manager->getChildIds($menu_plugin_id));
sort($expected_item['children']);
if ($child_ids) {
sort($child_ids);
}
$this->assertSame($expected_item['children'], $child_ids);
unset($expected_item['children']);
}
if (isset($expected_item['parents'])) {
$parent_ids = array_values($menu_link_manager->getParentIds($menu_plugin_id));
$this->assertSame($expected_item['parents'], $parent_ids);
unset($expected_item['parents']);
}
if (isset($expected_item['langcode']) && $entity) {
$this->assertEquals($expected_item['langcode'], $entity->langcode->value);
unset($expected_item['langcode']);
}
if (isset($expected_item['enabled']) && $entity) {
$this->assertEquals($expected_item['enabled'], $entity->enabled->value);
unset($expected_item['enabled']);
}
foreach ($expected_item as $key => $value) {
$this->assertNotNull($definition[$key]);
$this->assertSame($value, $definition[$key]);
}
}
}