diff --git a/.travis.yml b/.travis.yml index 54874b17fa..d6f1b00584 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ matrix: - php: 7.1 env: PHPUNIT_CONFIG='phpunit.xml' - php: 7.1 - env: BEHAT_OPTS="--profile=adminui --suite=adminui" SYMFONY_ENV=behat + env: BEHAT_OPTS="--profile=adminui --suite=adminui --no-interaction" SYMFONY_ENV=behat env: global: diff --git a/behat_suites.yml b/behat_suites.yml index 43dd92144e..59083b30e8 100644 --- a/behat_suites.yml +++ b/behat_suites.yml @@ -4,11 +4,16 @@ adminui: suites: adminui: paths: - - '%paths.base%/vendor/ezsystems/ezplatform-admin-ui/src/features' + - '%paths.base%/vendor/ezsystems/ezplatform-admin-ui/features' filters: tags: "@common" contexts: - - EzSystems\EzPlatformAdminUi\Behat\UtilityContext - - EzSystems\EzPlatformAdminUi\Behat\AuthenticationContext - - EzSystems\EzPlatformAdminUi\Behat\NavigationContext - - EzSystems\EzPlatformAdminUi\Behat\Hooks + - EzSystems\EzPlatformAdminUi\Behat\Helper\UtilityContext + - EzSystems\EzPlatformAdminUi\Behat\Helper\Hooks + - EzSystems\EzPlatformAdminUi\Behat\BusinessContext\AuthenticationContext + - EzSystems\EzPlatformAdminUi\Behat\BusinessContext\NavigationContext + - EzSystems\EzPlatformAdminUi\Behat\BusinessContext\AdministrationContext + - EzSystems\EzPlatformAdminUi\Behat\BusinessContext\ContentViewContext + - EzSystems\EzPlatformAdminUi\Behat\BusinessContext\UpdateContext + - EzSystems\EzPlatformAdminUi\Behat\BusinessContext\RightMenuContext + - EzSystems\EzPlatformAdminUi\Behat\BusinessContext\NotificationContext diff --git a/src/features/Authentication.feature b/features/Authentication.feature similarity index 100% rename from src/features/Authentication.feature rename to features/Authentication.feature diff --git a/features/ContentTypeGroup.feature b/features/ContentTypeGroup.feature new file mode 100644 index 0000000000..7533ef5950 --- /dev/null +++ b/features/ContentTypeGroup.feature @@ -0,0 +1,63 @@ +Feature: Content types management + As an administrator + In order to customize my eZ installation + I want to manage my Content types structure. + + Background: + Given I am logged as "admin" + And I go to "Content Types" in "Admin" tab + + @javascript @common + Scenario: Changes can be discarded while creating new Content Type Group + When I start creating new "Content Type Group" + And I set "Name" to "Test CTG" + And I click on the edit action bar button "Discard changes" + Then I should be on "Content Type Groups" page + And I should see "Content Type Groups" list + And there's no "Test CTG" on "Content Type Groups" list + + @javascript @common + Scenario: New Content Type Group can be added + When I start creating new "Content Type Group" + And I set "Name" to "Test CTG" + And I click on the edit action bar button "Create" + Then I should be on "Content Type Group" "Test CTG" page + + @javascript @common + Scenario: I can navigate to Admin / Content Types through breadcrumb + Given I go to "Test CTG" "Content Type Group" page + When I click on "Content Types" on breadcrumb + Then I should be on "Content Type Groups" page + And I should see "Content Type Groups" list + + @javascript @common + Scenario: Changes can be discarded while editing Content Type Group + Given there's "Test CTG" on "Content Type Groups" list + When I start editing "Content Type Group" "Test CTG" + And I set "Name" to "Test CTG edited" + And I click on the edit action bar button "Discard changes" + Then I should be on "Content Type Groups" page + And I should see "Content Type Groups" list + And there's "Test CTG" on "Content Type Groups" list + And there's no "Test CTG edited" on "Content Type Groups" list + + @javascript @common + Scenario: Content Type Group can be edited + Given there's "Test CTG" on "Content Type Groups" list + When I start editing "Content Type Group" "Test CTG" + And I set "Name" to "Test CTG edited" + And I click on the edit action bar button "Save" + Then I should be on "Content Type Group" "Test CTG edited" page + And notification that "Content type group" "Test CTG" is updated appears + + @javascript @common + Scenario: Content type group can be deleted + Given there's empty "Test CTG edited" on "Content Type Groups" list + When I delete "Content Type Group" "Test CTG edited" + Then there's no "Test CTG edited" on "Content Type Groups" list + And notification that "Content type group" "Test CTG edited" is deleted appears + + @javascript @common + Scenario: Non-empty Content type group cannot be deleted + Given there's non-empty "Content" on "Content Type Groups" list + Then "Content Type Group" "Content" cannot be selected diff --git a/src/lib/Behat/BusinessContext/AdministrationContext.php b/src/lib/Behat/BusinessContext/AdministrationContext.php new file mode 100644 index 0000000000..a554fedd35 --- /dev/null +++ b/src/lib/Behat/BusinessContext/AdministrationContext.php @@ -0,0 +1,175 @@ + ContentTypeGroupsPage::PAGE_NAME, + 'Content Type' => '', + 'Language' => '', + 'Role' => '', + 'Section' => '', + 'User' => '', + ]; + private $emptyHeaderMapping = [ + 'Content Type Groups' => 'Content Types count', + 'Sections' => 'Assigned Content items', + ]; + + /** + * @Then I should see :pageName list + * @Then I should see :pageName :parameter list + * + * @param string $pageName + */ + public function iSeeList(string $pageName, string $parameter = null): void + { + $contentTypeGroupsPage = PageObjectFactory::createPage($this->utilityContext, $pageName, $parameter); + $contentTypeGroupsPage->verifyElements(); + } + + /** + * @When I start creating new :newItemType + * + * @param string $newItemType + */ + public function iStartCreatingNew(string $newItemType): void + { + if (!array_key_exists($newItemType, $this->itemCreateMapping)) { + throw new \InvalidArgumentException(sprintf('Unrecognized item type name: %s', $newItemType)); + } + PageObjectFactory::createPage($this->utilityContext, $this->itemCreateMapping[$newItemType]) + ->adminList->clickPlusButton(); + } + + /** + * @Then there's :listElementName on :page list + * @Then there's :listElementName on :parameter :page list + */ + public function isElementOnTheList(string $listElementName, string $page): void + { + $isElementOnTheList = PageObjectFactory::createPage($this->utilityContext, $page) + ->adminList->isElementOnList($listElementName); + + if (!$isElementOnTheList) { + throw new ElementNotFoundException( + $this->utilityContext->getSession(), + sprintf('Element "%s" is not on the %s list.', $listElementName, $page) + ); + } + } + + /** + * @Then there's no :listElementName on :page list + * @Then there's no :listElementName on :parameter :page list + */ + public function isElementNotOnTheList(string $listElementName, string $page, string $parameter = null): void + { + $isElementOnTheList = PageObjectFactory::createPage($this->utilityContext, $page, $parameter) + ->adminList->isElementOnList($listElementName); + + if ($isElementOnTheList) { + throw new ElementNotFoundException( + $this->utilityContext->getSession(), + sprintf('Element "%s" is on the %s list.', $listElementName, $page) + ); + } + } + + /** + * Check if item is or is not empty, according to $empty param. + * + * @param string $itemName + * @param string $page + * @param string $shouldBeEmpty + */ + private function verifyContentsStatus(string $itemName, string $page, string $shouldBeEmpty): void + { + $emptyContainerCellValue = '0'; + + $contentsCount = PageObjectFactory::createPage($this->utilityContext, $page) + ->adminList->getListItemAttribute($itemName, $this->emptyHeaderMapping[$page]); + + $msg = ''; + if ($shouldBeEmpty) { + $msg = ' non'; + } + + if (($contentsCount !== $emptyContainerCellValue) === $shouldBeEmpty) { + throw new ElementNotFoundException( + $this->utilityContext->getSession(), + sprintf('No%s empty %s on the %s list.', $msg, $itemName, $page)); + } + } + + /** + * @Given there's empty :itemName on :page list + */ + public function isEmptyElementOnTheList(string $itemName, string $page): void + { + $this->verifyContentsStatus($itemName, $page, true); + } + + /** + * @Given there's non-empty :itemName on :page list + */ + public function isNonEmptyElementOnTheList(string $itemName, string $page): void + { + $this->verifyContentsStatus($itemName, $page, false); + } + + /** + * @Then :itemType :itemName cannot be selected + */ + public function itemCannotBeSelected(string $itemType, string $itemName): void + { + $isListElementSelectable = PageObjectFactory::createPage($this->utilityContext, $this->itemCreateMapping[$itemType]) + ->adminList->isListElementSelectable($itemName); + + if ($isListElementSelectable) { + throw new \Exception(sprintf('Element %s shoudn\'t be selectable.', $itemName)); + } + } + + /** + * @Given I go to :itemName :itemType page + */ + public function iGoToListItem(string $itemName, string $itemType): void + { + PageObjectFactory::createPage($this->utilityContext, $this->itemCreateMapping[$itemType]) + ->adminList->clickListElement($itemName); + } + + /** + * @When I start editing :itemType :itemName + */ + public function iStartEditingItem(string $itemType, string $itemName): void + { + PageObjectFactory::createPage($this->utilityContext, $this->itemCreateMapping[$itemType]) + ->adminList->clickEditButton($itemName); + } + + /** + * @When I delete :itemType :itemName + */ + public function iDeleteItem(string $itemType, string $itemName): void + { + $contentTypeGroups = PageObjectFactory::createPage($this->utilityContext, $this->itemCreateMapping[$itemType]); + $contentTypeGroups->adminList->selectListElement($itemName); + $contentTypeGroups->adminList->clickTrashButton(); + ElementFactory::createElement($this->utilityContext, Dialog::ELEMENT_NAME) + ->confirm(); + } +} diff --git a/src/lib/Behat/AuthenticationContext.php b/src/lib/Behat/BusinessContext/AuthenticationContext.php similarity index 90% rename from src/lib/Behat/AuthenticationContext.php rename to src/lib/Behat/BusinessContext/AuthenticationContext.php index f10123b31b..385fc4b9dc 100644 --- a/src/lib/Behat/AuthenticationContext.php +++ b/src/lib/Behat/BusinessContext/AuthenticationContext.php @@ -4,12 +4,13 @@ * @copyright Copyright (C) eZ Systems AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\EzPlatformAdminUi\Behat; +namespace EzSystems\EzPlatformAdminUi\Behat\BusinessContext; use EzSystems\EzPlatformAdminUi\Behat\PageObject\LoginPage; use EzSystems\EzPlatformAdminUi\Behat\PageObject\PageObjectFactory; use OutOfBoundsException; +/** Context for authentication actions */ class AuthenticationContext extends BusinessContext { /** @var array Dictionary of known user logins and their passwords */ @@ -34,7 +35,7 @@ public function iLoginAs(string $username, string $password): void * * @throws \OutOfBoundsException when username is not recognised */ - public function iAmLoggedAs(string $username) + public function iAmLoggedAs(string $username): void { $loginPage = PageObjectFactory::createPage($this->utilityContext, LoginPage::PAGE_NAME); $loginPage->open(); diff --git a/src/lib/Behat/BusinessContext.php b/src/lib/Behat/BusinessContext/BusinessContext.php similarity index 84% rename from src/lib/Behat/BusinessContext.php rename to src/lib/Behat/BusinessContext/BusinessContext.php index 8149562c83..f90356940d 100644 --- a/src/lib/Behat/BusinessContext.php +++ b/src/lib/Behat/BusinessContext/BusinessContext.php @@ -4,10 +4,11 @@ * @copyright Copyright (C) eZ Systems AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\EzPlatformAdminUi\Behat; +namespace EzSystems\EzPlatformAdminUi\Behat\BusinessContext; use Behat\Behat\Context\Context; use Behat\Behat\Hook\Scope\BeforeScenarioScope; +use EzSystems\EzPlatformAdminUi\Behat\Helper\UtilityContext; abstract class BusinessContext implements Context { diff --git a/src/lib/Behat/BusinessContext/ContentViewContext.php b/src/lib/Behat/BusinessContext/ContentViewContext.php new file mode 100644 index 0000000000..d347f6c237 --- /dev/null +++ b/src/lib/Behat/BusinessContext/ContentViewContext.php @@ -0,0 +1,32 @@ +utilityContext, ContentStructurePage::PAGE_NAME); + $contentStructurePage->createLandingPage($name, 'Test Desc'); + } + + /** + * @Then I (should) see :title title/topic + */ + public function iSeeTitle(string $title): void + { + $contentStructurePage = PageObjectFactory::createPage($this->utilityContext, ContentStructurePage::PAGE_NAME); + Assert::assertEquals($title, $contentStructurePage->getPageHeaderTitle()); + } +} diff --git a/src/lib/Behat/NavigationContext.php b/src/lib/Behat/BusinessContext/NavigationContext.php similarity index 52% rename from src/lib/Behat/NavigationContext.php rename to src/lib/Behat/BusinessContext/NavigationContext.php index 8929479c96..dcbe5e3e4f 100644 --- a/src/lib/Behat/NavigationContext.php +++ b/src/lib/Behat/BusinessContext/NavigationContext.php @@ -4,17 +4,20 @@ * @copyright Copyright (C) eZ Systems AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\EzPlatformAdminUi\Behat; +namespace EzSystems\EzPlatformAdminUi\Behat\BusinessContext; +use EzSystems\EzPlatformAdminUi\Behat\PageElement\Breadcrumb; +use EzSystems\EzPlatformAdminUi\Behat\PageElement\ElementFactory; use EzSystems\EzPlatformAdminUi\Behat\PageElement\UpperMenu; use EzSystems\EzPlatformAdminUi\Behat\PageObject\PageObjectFactory; +/** Context for general navigation actions */ class NavigationContext extends BusinessContext { /** * @Given I open :pageName page */ - public function openPage($pageName): void + public function openPage(string $pageName): void { $page = PageObjectFactory::createPage($this->utilityContext, $pageName); $page->open(); @@ -23,7 +26,7 @@ public function openPage($pageName): void /** * @Given I try to open :pageName page */ - public function tryToOpenPage($pageName): void + public function tryToOpenPage(string $pageName): void { $page = PageObjectFactory::createPage($this->utilityContext, $pageName); $page->open(false); @@ -31,10 +34,11 @@ public function tryToOpenPage($pageName): void /** * @Then I should be on :pageName page + * @Then I should be on :pageName :itemName page */ - public function iAmOnPage($pageName): void + public function iAmOnPage(string $pageName, string $itemName = null): void { - $page = PageObjectFactory::createPage($this->utilityContext, $pageName); + $page = PageObjectFactory::createPage($this->utilityContext, $pageName, $itemName); $page->verifyIsLoaded(); } @@ -42,13 +46,23 @@ public function iAmOnPage($pageName): void * @Then I go to :tab tab * @Then I go to :subTab in :tab tab */ - public function iGoToTab($tabName, $subTab = null): void + public function iGoToTab(string $tabName, string $subTab = null): void { - $upperMenu = new UpperMenu($this->utilityContext); + $upperMenu = ElementFactory::createElement($this->utilityContext, UpperMenu::ELEMENT_NAME); $upperMenu->goToTab($tabName); if ($subTab !== null) { $upperMenu->goToSubTab($subTab); } } + + /** + * @When I click on :element on breadcrumb + */ + public function iClickOnBreadcrumbLink(string $element): void + { + $breadcrumb = ElementFactory::createElement($this->utilityContext, Breadcrumb::ELEMENT_NAME); + $breadcrumb->verifyVisibility(); + $breadcrumb->clickBreadcrumbItem($element); + } } diff --git a/src/lib/Behat/BusinessContext/NotificationContext.php b/src/lib/Behat/BusinessContext/NotificationContext.php new file mode 100644 index 0000000000..b2adeb72ae --- /dev/null +++ b/src/lib/Behat/BusinessContext/NotificationContext.php @@ -0,0 +1,26 @@ +utilityContext, Notification::ELEMENT_NAME); + $notification->verifyVisibility(); + $notification->verifyAlertSuccess(); + Assert::assertEquals(sprintf('%s \'%s\' %s.', $itemType, $itemName, $action), $notification->getMessage()); + } +} diff --git a/src/lib/Behat/BusinessContext/RightMenuContext.php b/src/lib/Behat/BusinessContext/RightMenuContext.php new file mode 100644 index 0000000000..e22f8b72bd --- /dev/null +++ b/src/lib/Behat/BusinessContext/RightMenuContext.php @@ -0,0 +1,25 @@ +utilityContext); + $rightMenu->clickButton($button); + } +} diff --git a/src/lib/Behat/BusinessContext/UpdateContext.php b/src/lib/Behat/BusinessContext/UpdateContext.php new file mode 100644 index 0000000000..86cf1e2777 --- /dev/null +++ b/src/lib/Behat/BusinessContext/UpdateContext.php @@ -0,0 +1,24 @@ +utilityContext, UpdateItemPage::PAGE_NAME) + ->updateForm->fillFIeldWithValue($field, $value); + } +} diff --git a/src/lib/Behat/ContentContext.php b/src/lib/Behat/ContentContext.php deleted file mode 100644 index 8be2000ba6..0000000000 --- a/src/lib/Behat/ContentContext.php +++ /dev/null @@ -1,60 +0,0 @@ -utilityContext); - $rightMenu->clickButton($button); - } - - /** - * @Given I start creating a new Landing Page :name - */ - public function startCreatingNewLandingPage($name) - { - $contentStructurePage = PageObjectFactory::createPage($this->utilityContext, ContentStructurePage::PAGE_NAME); - $contentStructurePage->createLandingPage($name, 'Test Desc'); - } - - /** - * @Then I (should) see :title title/topic - */ - public function iSeeTitle($title) - { - $contentStructurePage = PageObjectFactory::createPage($this->utilityContext, ContentStructurePage::PAGE_NAME); - Assert::assertEquals($title, $contentStructurePage->getPageHeaderTitle()); - } - - /** - * @When I set :field to :value - * @When I set :field as empty - */ - public function fillFieldWithValue($field, $value = '') - { - $fieldNode = $this->utilityContext->waitUntil(10, - function () use ($field) { - return $this->utilityContext->getSession()->getPage()->findField($field); - }); - - $fieldNode->setValue(''); - $fieldNode->setValue($value); - } -} diff --git a/src/lib/Behat/EnvironmentRestore.php b/src/lib/Behat/Helper/EnvironmentRestore.php similarity index 98% rename from src/lib/Behat/EnvironmentRestore.php rename to src/lib/Behat/Helper/EnvironmentRestore.php index 0fe9815590..b41ba16f13 100644 --- a/src/lib/Behat/EnvironmentRestore.php +++ b/src/lib/Behat/Helper/EnvironmentRestore.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) eZ Systems AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\EzPlatformAdminUi\Behat; +namespace EzSystems\EzPlatformAdminUi\Behat\Helper; use EzSystems\PlatformInstallerBundle\Installer\Installer; use Symfony\Component\Console\Output\NullOutput; diff --git a/src/lib/Behat/Hooks.php b/src/lib/Behat/Helper/Hooks.php similarity index 95% rename from src/lib/Behat/Hooks.php rename to src/lib/Behat/Helper/Hooks.php index 8e5cd50236..2d2ed02eb1 100644 --- a/src/lib/Behat/Hooks.php +++ b/src/lib/Behat/Helper/Hooks.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) eZ Systems AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\EzPlatformAdminUi\Behat; +namespace EzSystems\EzPlatformAdminUi\Behat\Helper; use Behat\MinkExtension\Context\RawMinkContext; use Behat\Symfony2Extension\Context\KernelDictionary; diff --git a/src/lib/Behat/StudioUtility.php b/src/lib/Behat/Helper/StudioUtility.php similarity index 99% rename from src/lib/Behat/StudioUtility.php rename to src/lib/Behat/Helper/StudioUtility.php index 2fd02dbe46..1d7c27cea2 100644 --- a/src/lib/Behat/StudioUtility.php +++ b/src/lib/Behat/Helper/StudioUtility.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) eZ Systems AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\EzPlatformAdminUi\Behat; +namespace EzSystems\EzPlatformAdminUi\Behat\Helper; use Behat\Mink\Element\NodeElement; use Exception; diff --git a/src/lib/Behat/UtilityContext.php b/src/lib/Behat/Helper/UtilityContext.php similarity index 82% rename from src/lib/Behat/UtilityContext.php rename to src/lib/Behat/Helper/UtilityContext.php index da2d54a3dc..6c15565420 100644 --- a/src/lib/Behat/UtilityContext.php +++ b/src/lib/Behat/Helper/UtilityContext.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) eZ Systems AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\EzPlatformAdminUi\Behat; +namespace EzSystems\EzPlatformAdminUi\Behat\Helper; use Behat\Mink\Element\NodeElement; use Behat\Mink\Element\TraversableElement; @@ -99,6 +99,41 @@ public function getElementByText(string $text, string $selector, string $textSel return null; } + /** + * Finds an HTML element by class and the text value and returns it's position in order. Search can be narrowed to children of baseElement. + * + * @param string $text Text value of the element + * @param string $selector CSS selector of the element + * @param string $textSelector Extra CSS selector for text of the element + * @param TraversableElement|null $baseElement + * + * @return int + */ + public function getElementPositionByText(string $text, string $selector, string $textSelector = null, TraversableElement $baseElement = null): int + { + $baseElement = $baseElement ?? $this->getSession()->getPage(); + $counter = 0; + + $elements = $this->findAllWithWait($selector, $baseElement); + foreach ($elements as $element) { + ++$counter; + if ($textSelector !== null) { + try { + $elementText = $this->findElement($textSelector, 10, $element)->getText(); + } catch (\Exception $e) { + continue; + } + } else { + $elementText = $element->getText(); + } + if ($elementText === $text) { + return $counter; + } + } + + return 0; + } + /** * Waits no longer than specified timeout for the given condition to be true. * diff --git a/src/lib/Behat/PageElement/AdminList.php b/src/lib/Behat/PageElement/AdminList.php new file mode 100644 index 0000000000..1fbbcf37df --- /dev/null +++ b/src/lib/Behat/PageElement/AdminList.php @@ -0,0 +1,102 @@ + 'form', + 'plusButton' => '.ez-icon-create', + 'trashButton' => '.ez-icon-trash', + 'editButton' => 'tr:nth-child(%s) a[title=Edit]', + 'listHeader' => '.ez-table-header__headline', + 'tableHeader' => 'th', + 'listElementLink' => '.ez-checkbox-cell+ td a', + 'tableCell' => 'tr:nth-child(%s) td:nth-child(%s)', + ]; + + /** @var string Name by which Element is recognised */ + public const ELEMENT_NAME = 'Admin List'; + /** @var string list table title placed in the blue bar */ + protected $listHeader; + + public function __construct(UtilityContext $context, string $listHeader) + { + parent::__construct($context); + $this->listHeader = $listHeader; + } + + private function verifyProperList(): void + { + $actualHeader = $this->context->findElement($this->fields['listHeader'], $this->defaultTimeout)->getText(); + + if ($this->listHeader !== $actualHeader) { + throw new ElementNotFoundException($this->context->getSession(), 'table header', $this->fields['listHeader']); + } + } + + public function verifyVisibility(): void + { + $this->context->waitUntilElementIsVisible($this->fields['plusButton'], $this->defaultTimeout); + $this->context->waitUntilElementIsVisible($this->fields['trashButton'], $this->defaultTimeout); + $this->context->waitUntilElementIsVisible($this->fields['listHeader'], $this->defaultTimeout); + + $this->verifyProperList(); + } + + public function clickPlusButton(): void + { + $this->context->findElement($this->fields['plusButton'])->click(); + } + + public function clickTrashButton(): void + { + $this->context->findElement($this->fields['trashButton'])->click(); + } + + public function isListElementSelectable(string $name): bool + { + $position = $this->context->getElementPositionByText($name, $this->fields['listElementLink']); + $checkbox = $this->context->findElement(sprintf($this->fields['tableCell'], $position, 1) . ' .form-check-input')->getAttribute('disabled'); + + return $checkbox !== 'disabled'; + } + + public function selectListElement(string $name): void + { + $position = $this->context->getElementPositionByText($name, $this->fields['listElementLink']); + $this->context->findElement(sprintf($this->fields['tableCell'], $position, 1))->checkField(''); + } + + public function clickListElement(string $name): void + { + $this->context->getElementByText($name, $this->fields['listElementLink'])->click(); + } + + public function isElementOnList(string $name): bool + { + return $this->context->getElementByText($name, $this->fields['listElementLink']) !== null; + } + + public function getListItemAttribute(string $name, string $header): string + { + $columnPosition = $this->context->getElementPositionByText($header, $this->fields['tableHeader'], null, $this->context->findElement($this->fields['list'])); + $rowPosition = $this->context->getElementPositionByText($name, $this->fields['listElementLink'], null, $this->context->findElement($this->fields['list'])); + + return $this->context->findElement(sprintf($this->fields['tableCell'], $rowPosition, $columnPosition))->getText(); + } + + public function clickEditButton(string $listItemName): void + { + $position = $this->context->getElementPositionByText($listItemName, $this->fields['listElementLink']); + $this->context->findElement(sprintf($this->fields['editButton'], $position))->click(); + } +} diff --git a/src/lib/Behat/PageElement/Breadcrumb.php b/src/lib/Behat/PageElement/Breadcrumb.php new file mode 100644 index 0000000000..00281daf51 --- /dev/null +++ b/src/lib/Behat/PageElement/Breadcrumb.php @@ -0,0 +1,28 @@ + '.breadcrumb-item', + 'breadcrumbItemLink' => '.breadcrumb-item a', + ]; + /** @var string Name by which Element is recognised */ + public const ELEMENT_NAME = 'Breadcrumb'; + + public function verifyVisibility(): void + { + $this->context->waitUntilElementIsVisible($this->fields['breadcrumbItem']); + } + + public function clickBreadcrumbItem(string $itemName): void + { + $this->context->getElementByText($itemName, $this->fields['breadcrumbItemLink'])->click(); + } +} diff --git a/src/lib/Behat/PageElement/Dialog.php b/src/lib/Behat/PageElement/Dialog.php new file mode 100644 index 0000000000..bd167c88dd --- /dev/null +++ b/src/lib/Behat/PageElement/Dialog.php @@ -0,0 +1,33 @@ + '.btn--trigger', + 'decline' => '.btn--no', + ]; + /** @var string Name by which Element is recognised */ + public const ELEMENT_NAME = 'Dialog'; + + public function confirm(): void + { + $this->context->findElement($this->fields['confirm'])->click(); + } + + public function decline(): void + { + $this->context->findElement($this->fields['decline'])->click(); + } + + public function verifyVisibility(): void + { + // TODO: Implement verifyVisibility() method. Not sure if it's needed + } +} diff --git a/src/lib/Behat/PageElement/Element.php b/src/lib/Behat/PageElement/Element.php index f8c8aafd73..bab496dce7 100644 --- a/src/lib/Behat/PageElement/Element.php +++ b/src/lib/Behat/PageElement/Element.php @@ -6,8 +6,9 @@ */ namespace EzSystems\EzPlatformAdminUi\Behat\PageElement; -use EzSystems\EzPlatformAdminUi\Behat\UtilityContext; +use EzSystems\EzPlatformAdminUi\Behat\Helper\UtilityContext; +/** Abstract for pages elements */ abstract class Element { /** @var int */ @@ -15,13 +16,12 @@ abstract class Element /* \EzSystems\EzPlatformAdminUi\Behat\UtilityContext */ protected $context; + protected $fields; public function __construct(UtilityContext $context) { $this->context = $context; } - public function verifyVisibility(): void - { - } + abstract public function verifyVisibility(): void; } diff --git a/src/lib/Behat/PageElement/ElementFactory.php b/src/lib/Behat/PageElement/ElementFactory.php new file mode 100644 index 0000000000..3f8de11141 --- /dev/null +++ b/src/lib/Behat/PageElement/ElementFactory.php @@ -0,0 +1,42 @@ + '.alert', + 'successAlert' => '.alert-success', + ]; + /** @var string Name by which Element is recognised */ + public const ELEMENT_NAME = 'Notification'; + + public function verifyVisibility(): void + { + $this->context->waitUntilElementIsVisible($this->fields['alert'], $this->defaultTimeout); + } + + public function verifyAlertSuccess(): void + { + $this->context->assertElementOnPage($this->fields['successAlert']); + } + + public function getMessage(): string + { + return $this->context->findElement($this->fields['alert'])->getText(); + } +} diff --git a/src/lib/Behat/PageElement/RightMenu.php b/src/lib/Behat/PageElement/RightMenu.php index e53a7851b7..018702687b 100644 --- a/src/lib/Behat/PageElement/RightMenu.php +++ b/src/lib/Behat/PageElement/RightMenu.php @@ -6,15 +6,24 @@ */ namespace EzSystems\EzPlatformAdminUi\Behat\PageElement; +/** Element that describes right action menu (Create, Preview, Publish etc.) */ class RightMenu extends Element { + /** @var string Name by which Element is recognised */ + public const ELEMENT_NAME = 'Right Menu'; + /** - * Clicks a button on the right menu (Create, Preview, Publish etc.). + * Clicks a button on the right menu. * * @param $buttonName */ - public function clickButton($buttonName) + public function clickButton(string $buttonName): void { $this->context->getElementByText($buttonName, '.ez-context-menu .btn-block')->click(); } + + public function verifyVisibility(): void + { + // TODO: Implement verifyVisibility() method. + } } diff --git a/src/lib/Behat/PageElement/UpdateForm.php b/src/lib/Behat/PageElement/UpdateForm.php new file mode 100644 index 0000000000..2dbd805eb6 --- /dev/null +++ b/src/lib/Behat/PageElement/UpdateForm.php @@ -0,0 +1,36 @@ + '.form-group', + 'mainFormSection' => '.px-5:nth-child(1) .card-body', + ]; + + /** @var string Name by which Element is recognised */ + public const ELEMENT_NAME = 'Admin Update Form'; + public const MAIN_FORM_SECTION = 'mainFormSection'; + + public function verifyVisibility(): void + { + // TODO: Implement verifyVisibility() method. + } + + public function fillFIeldWithValue(string $fieldName, string $value): void + { + $fieldNode = $this->context->waitUntil($this->defaultTimeout, + function () use ($fieldName) { + return $this->context->getSession()->getPage()->findField($fieldName); + }); + + $fieldNode->setValue(''); + $fieldNode->setValue($value); + } +} diff --git a/src/lib/Behat/PageElement/UpperMenu.php b/src/lib/Behat/PageElement/UpperMenu.php index f5ab4b7811..d3e3d3817a 100644 --- a/src/lib/Behat/PageElement/UpperMenu.php +++ b/src/lib/Behat/PageElement/UpperMenu.php @@ -6,14 +6,18 @@ */ namespace EzSystems\EzPlatformAdminUi\Behat\PageElement; +/** Element that describes upper menu (Content, Admin, Page and theirs children) */ class UpperMenu extends Element { + /** @var string Name by which Element is recognised */ + public const ELEMENT_NAME = 'Upper Menu'; + /** * Clicks on top menu, for example "Content" tab. * * @param $tabName */ - public function goToTab($tabName) + public function goToTab(string $tabName): void { $this->context->getElementByText($tabName, '.nav-link')->click(); } @@ -23,8 +27,13 @@ public function goToTab($tabName) * * @param $tabName */ - public function goToSubTab($tabName) + public function goToSubTab(string $tabName): void { $this->context->getElementByText($tabName, '.navbar-expand-lg .nav-link')->click(); } + + public function verifyVisibility(): void + { + // TODO: Implement verifyVisibility() method. + } } diff --git a/src/lib/Behat/PageObject/ContentStructurePage.php b/src/lib/Behat/PageObject/ContentStructurePage.php index 792d1261b1..b367ea5fa1 100644 --- a/src/lib/Behat/PageObject/ContentStructurePage.php +++ b/src/lib/Behat/PageObject/ContentStructurePage.php @@ -7,7 +7,7 @@ namespace EzSystems\EzPlatformAdminUi\Behat\PageObject; use EzSystems\EzPlatformAdminUi\Behat\PageElement\RightMenu; -use EzSystems\EzPlatformAdminUi\Behat\UtilityContext; +use EzSystems\EzPlatformAdminUi\Behat\Helper\UtilityContext; class ContentStructurePage extends Page { @@ -26,7 +26,7 @@ public function __construct(UtilityContext $context) $this->rightMenu = new RightMenu($this->context); } - public function createLandingPage($name, $description) + public function createLandingPage(string $name, string $description): void { $this->startCreatingContent('Landing page'); $this->context->getSession()->getPage()->findField('Title')->setValue($name); @@ -38,9 +38,14 @@ public function createLandingPage($name, $description) * * @param $contentType */ - public function startCreatingContent($contentType) + public function startCreatingContent(string $contentType): void { $this->rightMenu->clickButton('Create'); $this->context->getElementByText($contentType, '.form-check-label')->click(); } + + public function verifyElements(): void + { + // TODO: Implement verifyElements() method. + } } diff --git a/src/lib/Behat/PageObject/ContentTypeGroupPage.php b/src/lib/Behat/PageObject/ContentTypeGroupPage.php new file mode 100644 index 0000000000..8b9485e72a --- /dev/null +++ b/src/lib/Behat/PageObject/ContentTypeGroupPage.php @@ -0,0 +1,41 @@ +groupName = $groupName; + parent::__construct($context); + $this->adminList = ElementFactory::createElement($this->context, AdminList::ELEMENT_NAME, sprintf('Content Types in %s', $this->groupName)); + } + + /** + * Verifies that all necessary elements are visible. + */ + public function verifyElements(): void + { + $this->adminList->verifyVisibility(); + } +} diff --git a/src/lib/Behat/PageObject/ContentTypeGroupsPage.php b/src/lib/Behat/PageObject/ContentTypeGroupsPage.php new file mode 100644 index 0000000000..6c2c12ce89 --- /dev/null +++ b/src/lib/Behat/PageObject/ContentTypeGroupsPage.php @@ -0,0 +1,38 @@ +adminList = ElementFactory::createElement($this->context, AdminList::ELEMENT_NAME, self::PAGE_NAME); + } + + /** + * Verifies that all necessary elements are visible. + */ + public function verifyElements(): void + { + $this->adminList->verifyVisibility(); + } +} diff --git a/src/lib/Behat/PageObject/DashboardPage.php b/src/lib/Behat/PageObject/DashboardPage.php index f6bd1b2388..6ee26653d7 100644 --- a/src/lib/Behat/PageObject/DashboardPage.php +++ b/src/lib/Behat/PageObject/DashboardPage.php @@ -14,7 +14,7 @@ class DashboardPage extends Page protected $route = '/admin/dashboard'; /** @var string Name by which Page is recognised */ - const PAGE_NAME = 'Dashboard'; + public const PAGE_NAME = 'Dashboard'; /** * Verifies that the Dashboard has the "Me" section. diff --git a/src/lib/Behat/PageObject/LoginPage.php b/src/lib/Behat/PageObject/LoginPage.php index 9c02468e04..a4b75daa1e 100644 --- a/src/lib/Behat/PageObject/LoginPage.php +++ b/src/lib/Behat/PageObject/LoginPage.php @@ -12,9 +12,12 @@ class LoginPage extends Page protected $route = '/admin/login'; /** @var string Name by which Page is recognised */ - const PAGE_NAME = 'Login'; + public const PAGE_NAME = 'Login'; - protected $fields = ['username' => '#username', 'password' => '#password']; + protected $fields = [ + 'username' => '#username', + 'password' => '#password', + ]; /** * Performs login action. diff --git a/src/lib/Behat/PageObject/Page.php b/src/lib/Behat/PageObject/Page.php index ab6e24e24a..014cf5f05a 100644 --- a/src/lib/Behat/PageObject/Page.php +++ b/src/lib/Behat/PageObject/Page.php @@ -6,11 +6,11 @@ */ namespace EzSystems\EzPlatformAdminUi\Behat\PageObject; -use EzSystems\EzPlatformAdminUi\Behat\UtilityContext; +use EzSystems\EzPlatformAdminUi\Behat\Helper\UtilityContext; abstract class Page { - protected $defaultTimeout = 5; + protected $defaultTimeout = 50; /** @var string Route under which the Page is available */ protected $route; @@ -18,6 +18,9 @@ abstract class Page /** @var UtilityContext context for interactions with the page */ protected $context; + // @var UpperMenu + public $upperMenu; + public function __construct(UtilityContext $context) { $this->context = $context; @@ -59,9 +62,7 @@ public function verifyRoute(): void /** * Verifies that expected elements are present. */ - public function verifyElements(): void - { - } + abstract public function verifyElements(): void; /** * Gets the header text displayed in AdminUI. diff --git a/src/lib/Behat/PageObject/PageObjectFactory.php b/src/lib/Behat/PageObject/PageObjectFactory.php index 9de345c8bf..f0a90f8e6e 100644 --- a/src/lib/Behat/PageObject/PageObjectFactory.php +++ b/src/lib/Behat/PageObject/PageObjectFactory.php @@ -6,7 +6,7 @@ */ namespace EzSystems\EzPlatformAdminUi\Behat\PageObject; -use EzSystems\EzPlatformAdminUi\Behat\UtilityContext; +use EzSystems\EzPlatformAdminUi\Behat\Helper\UtilityContext; use EzSystems\StudioUIBundle\Tests\Behat\PageObject\LandingPageEditorPage; class PageObjectFactory @@ -16,10 +16,11 @@ class PageObjectFactory * * @param UtilityContext $context * @param string $pageName Name of the Page to creator + * @param string $parameter additional parameter for constructor, e.g. name of item * - * @return Page Page to interact with + * @return LoginPage|DashboardPage|ContentStructurePage|ContentTypeGroupsPage|UpdateItemPage to interact with */ - public static function createPage(UtilityContext $context, string $pageName): Page + public static function createPage(UtilityContext $context, string $pageName, ?string ...$parameters): Page { switch ($pageName) { case LoginPage::PAGE_NAME: @@ -28,6 +29,12 @@ public static function createPage(UtilityContext $context, string $pageName): Pa return new DashboardPage($context); case ContentStructurePage::PAGE_NAME: return new ContentStructurePage($context); + case ContentTypeGroupsPage::PAGE_NAME: + return new ContentTypeGroupsPage($context); + case UpdateItemPage::PAGE_NAME: + return new UpdateItemPage($context); + case ContentTypeGroupPage::PAGE_NAME: + return new ContentTypeGroupPage($context, $parameters[0]); case LandingPageEditorPage::PAGE_NAME: return new LandingPageEditorPage($context); default: diff --git a/src/lib/Behat/PageObject/UpdateItemPage.php b/src/lib/Behat/PageObject/UpdateItemPage.php new file mode 100644 index 0000000000..e11b39dd24 --- /dev/null +++ b/src/lib/Behat/PageObject/UpdateItemPage.php @@ -0,0 +1,44 @@ +updateForm = ElementFactory::createElement($this->context, UpdateForm::ELEMENT_NAME); + $this->rightMenu = ElementFactory::createElement($this->context, RightMenu::ELEMENT_NAME); + } + + public function verifyElements(): void + { + $this->rightMenu->verifyVisibility(); + $this->updateForm->verifyVisibility(); + } +}