From 672d09b85f89949450a5f186f08d6c616b025c4b Mon Sep 17 00:00:00 2001 From: richardgaunt <57734756+richardgaunt@users.noreply.github.com> Date: Fri, 19 Mar 2021 14:13:37 +1100 Subject: [PATCH] Added menu and menu link traits. (#67) --- src/D8/MenuTrait.php | 171 ++++++++++++++++++++- src/SelectTrait.php | 22 +++ tests/behat/bootstrap/FeatureContextD8.php | 2 + tests/behat/features/d8.menu.feature | 45 ++++++ 4 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 tests/behat/features/d8.menu.feature diff --git a/src/D8/MenuTrait.php b/src/D8/MenuTrait.php index 7d17f1b0..9c10a317 100644 --- a/src/D8/MenuTrait.php +++ b/src/D8/MenuTrait.php @@ -2,7 +2,9 @@ namespace IntegratedExperts\BehatSteps\D8; +use Behat\Behat\Hook\Scope\AfterScenarioScope; use Behat\Gherkin\Node\TableNode; +use Drupal\menu_link_content\Entity\MenuLinkContent; use Drupal\system\Entity\Menu; /** @@ -13,17 +15,180 @@ trait MenuTrait { /** - * Remove menu. + * Menus. + * + * @var \Drupal\system\Entity\Menu[] + */ + protected $menus = []; + + /** + * Menu links. + * + * @var \Drupal\menu_link_content\Entity\MenuLinkContent[] + */ + protected $menuLinks = []; + + /** + * Remove menu by menu name. + * + * Provide menu labels in the following format: + * | Fish Menu | + * | ... | * * @Given no menus: */ public function menuDelete(TableNode $table) { - foreach ($table->getColumn(0) as $name) { - $menu = Menu::load($name); + foreach ($table->getColumn(0) as $label) { + $menu = $this->loadMenuByLabel($label); if ($menu) { $menu->delete(); } } } + /** + * Create a menu if one does not exist. + * + * Provide menu data in the following format: + * + * | label | description | + * | Fish Menu | Menu of fish | + * | ... | ... | + * + * @Given menus: + */ + public function menuCreate(TableNode $table) { + foreach ($table->getHash() as $menu_hash) { + if (empty($menu_hash['id'])) { + // Create menu id if one not provided. + $menu_id = strtolower($menu_hash['label']); + $menu_id = preg_replace('/[^a-z0-9_]+/', '_', $menu_id); + $menu_id = preg_replace('/_+/', '_', $menu_id); + $menu_hash['id'] = $menu_id; + } + $menu = Menu::create($menu_hash); + $menu->save(); + $this->menus[] = $menu; + } + } + + /** + * Remove menu links by title. + * + * Provide menu link titles in the following format: + * | Test Menu | + * | ... | + * + * @Given no :menu_name menu_links: + */ + public function menuLinksDelete($menu_name, TableNode $table) { + foreach ($table->getColumn(0) as $title) { + $menu_link = $this->loadMenuLinkByTitle($title, $menu_name); + if ($menu_link) { + $menu_link->delete(); + } + } + } + + /** + * Create menu links. + * + * Provide menu link data in the following format: + * + * | title | enabled | uri | parent | + * | Parent Link | 1 | https://www.example.com | | + * | Child Link | 1 | https://www.example.com | Parent Link | + * | ... | ... | ... | ... | + * + * @Given :menu_name menu_links: + */ + public function menuLinksCreate($menu_name, TableNode $table) { + $menu = $this->loadMenuByLabel($menu_name); + foreach ($table->getHash() as $menu_link_hash) { + $menu_link_hash['menu_name'] = $menu->id(); + // Add uri to correct property. + $menu_link_hash['link']['uri'] = $menu_link_hash['uri']; + unset($menu_link_hash['uri']); + // Create parent property in format required. + if (!empty($menu_link_hash['parent'])) { + $parent_link = $this->loadMenuLinkByTitle($menu_link_hash['parent'], $menu_name); + $menu_link_hash['parent'] = 'menu_link_content:' . $parent_link->uuid(); + } + else { + unset($menu_link_hash['parent']); + } + $menu_link = MenuLinkContent::create($menu_link_hash); + $menu_link->save(); + $this->menuLinks[] = $menu_link; + } + } + + /** + * @AfterScenario + */ + public function menuCleanAll(AfterScenarioScope $scope) { + // Allow to skip this by adding a tag. + if ($scope->getScenario()->hasTag('behat-steps-skip:' . __FUNCTION__)) { + return; + } + // Clean up created menus. + foreach ($this->menus as $menu) { + try { + $menu->delete(); + } + catch (\Exception $exception) { + // Ignore the exception and move on. + continue; + } + } + // Clean up menu links. + foreach ($this->menuLinks as $menu_link) { + try { + $menu_link->delete(); + } + catch (\Exception $exception) { + // Ignore the exception and move on. + continue; + } + } + + $this->menuLinks = []; + + $this->menus = []; + } + + /** + * Gets a menu by label. + */ + protected function loadMenuByLabel($label) { + /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */ + $entity_type_manager = \Drupal::getContainer()->get('entity_type.manager'); + $query = $entity_type_manager->getStorage('menu')->getQuery(); + $query->condition('label', $label); + $menu_ids = $query->execute(); + $menu_id = reset($menu_ids); + if ($menu_id === FALSE) { + throw new \Exception(sprintf('Could not find the %s menu.', $label)); + } + + return Menu::load($menu_id); + } + + /** + * Gets a menu link by title and menu name. + */ + protected function loadMenuLinkByTitle($title, $menu_name) { + $menu = $this->loadMenuByLabel($menu_name); + /** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */ + $entity_type_manager = \Drupal::getContainer()->get('entity_type.manager'); + $query = $entity_type_manager->getStorage('menu_link_content')->getQuery(); + $menu_link_ids = $query->condition('menu_name', $menu->id())->condition('title', $title)->execute(); + $menu_link_id = reset($menu_link_ids); + if ($menu_link_id === FALSE) { + throw new \Exception(sprintf('Could not find the %s menu link in %s menu.', $title, $menu_name)); + } + + return MenuLinkContent::load($menu_link_id); + } + } diff --git a/src/SelectTrait.php b/src/SelectTrait.php index 0dd3361c..079f6176 100644 --- a/src/SelectTrait.php +++ b/src/SelectTrait.php @@ -22,6 +22,8 @@ public function selectShouldHaveOption($select, $option) { if (is_null($optionElement)) { throw new \InvalidArgumentException(sprintf('Option "%s" is not found in select "%s".', $option, $select)); } + + return $optionElement; } /** @@ -39,4 +41,24 @@ public function selectShouldNotHaveOption($select, $option) { } } + /** + * @Then select :select should have option :option selected + */ + public function selectShouldHaveOptionSelected($select, $option) { + $optionElement = $this->selectShouldHaveOption($select, $option); + if ($optionElement->getAttribute('selected') !== 'selected') { + throw new \InvalidArgumentException(sprintf('Option "%s" in select "%s" is not selected.', $option, $select)); + } + } + + /** + * @Then select :select should not have option :option selected + */ + public function selectShouldHaveNotOptionSelected($select, $option) { + $optionElement = $this->selectShouldHaveOption($select, $option); + if ($optionElement->getAttribute('selected') === 'selected') { + throw new \InvalidArgumentException(sprintf('Option "%s" in select "%s" is selected, but should not.', $option, $select)); + } + } + } diff --git a/tests/behat/bootstrap/FeatureContextD8.php b/tests/behat/bootstrap/FeatureContextD8.php index 20b94da2..a6d4e507 100644 --- a/tests/behat/bootstrap/FeatureContextD8.php +++ b/tests/behat/bootstrap/FeatureContextD8.php @@ -39,6 +39,7 @@ use IntegratedExperts\BehatSteps\LinkTrait; use IntegratedExperts\BehatSteps\PathTrait; use IntegratedExperts\BehatSteps\ResponseTrait; +use IntegratedExperts\BehatSteps\SelectTrait; /** * Defines application features from the specific context. @@ -60,6 +61,7 @@ class FeatureContextD8 extends DrupalContext { use PathTrait; use ResponseTrait; use RoleTrait; + use SelectTrait; use TaxonomyTrait; use TestmodeTrait; use UserTrait; diff --git a/tests/behat/features/d8.menu.feature b/tests/behat/features/d8.menu.feature new file mode 100644 index 00000000..360810ab --- /dev/null +++ b/tests/behat/features/d8.menu.feature @@ -0,0 +1,45 @@ +@d8 +Feature: Check that MenuTrait works for D8 + + @api + Scenario: Assert "Given menus:" + Given menus: + | label | description | + | [TEST] menu 1 title | Test menu 1 description | + | [TEST] menu 2 title | Test menu 2 description | + And I am logged in as a user with the "administrator" role + When I visit "/admin/structure/menu" + Then I should see the text "[TEST] menu 1 title" + And I should see the text "[TEST] menu 2 title" + And I should see the text "Test menu 1 description" + And I should see the text "Test menu 2 description" + + @api + Scenario: Assert "Given no menus:" + Given menus: + | label | description | + | [TEST] menu 1 title | Test menu 1 description | + | [TEST] menu 2 title | Test menu 2 description | + Given no menus: + | [TEST] menu 1 title | + | [TEST] menu 2 title | + And I am logged in as a user with the "administrator" role + When I visit "/admin/structure/menu" + Then I should not see the text "[TEST] menu 1 title" + And I should not see the text "[TEST] menu 2 title" + And I should not see the text "Test menu 1 description" + And I should not see the text "Test menu 2 description" + + @api + Scenario: Assert "Given menu_links:" + Given menus: + | label | description | + | [TEST] menu 1 title | Test menu 1 description | + Given "[TEST] menu 1 title" menu_links: + | title | enabled | uri | parent | + | Parent Link Title | 1 | https://www.example.com | | + | Child Link Title | 1 | https://www.example.com | Parent Link Title | + And I am logged in as a user with the "administrator" role + When I visit "/admin/structure/menu/manage/_test_menu_1_title" + And I should see "Parent Link Title" + And I should see "Child Link Title"