From 18ddca0be9d1fdb3a5f4c2f7c2c2580432d40941 Mon Sep 17 00:00:00 2001 From: Maxime Rainville Date: Thu, 10 Aug 2023 23:41:11 +1200 Subject: [PATCH] Add basic ManyANy behat test --- tests/behat/features/manage-link-list.feature | 28 +-- .../behat/features/manage-single-link.feature | 1 + tests/behat/src/AnyFieldContextTrait.php | 180 +++++++++++++++++ tests/behat/src/FixtureContext.php | 190 ++++-------------- tests/behat/src/ManyAnyFieldContextTrait.php | 180 +++++++++++++++++ 5 files changed, 410 insertions(+), 169 deletions(-) create mode 100644 tests/behat/src/AnyFieldContextTrait.php create mode 100644 tests/behat/src/ManyAnyFieldContextTrait.php diff --git a/tests/behat/features/manage-link-list.feature b/tests/behat/features/manage-link-list.feature index f87276b..ed77273 100644 --- a/tests/behat/features/manage-link-list.feature +++ b/tests/behat/features/manage-link-list.feature @@ -5,7 +5,7 @@ Feature: Manage a list of items Background: Given a "page" "About Us" has the "Content" "

My content

" - Given a "page" "Contact us" has the "Content" "

Contact details

" + Given a "page" "Contact us" has the "Content" "

Contact details

" And a "image" "assets/file2.jpg" And the "group" "EDITOR" has permissions "Access to 'Pages' section" and "SITETREE_GRANT_ACCESS" and "SITETREE_REORGANISE" And I am logged in as a member of "EDITOR" group @@ -15,22 +15,24 @@ Feature: Manage a list of items And I should see an edit page form And I click the "Link test" CMS tab - Scenario: I can fill an empty AnyField with a link - And I should see an empty "My test link" AnyField - Then I edit the "My test link" AnyField - And I should see an option to add a "Site Tree Link" item to the "My test link" AnyField - And I should see an option to add a "External Link" item to the "My test link" AnyField - And I should see an option to add a "File Link" item to the "My test link" AnyField - And I should see an option to add a "Email Link" item to the "My test link" AnyField - And I should see an option to add a "Phone Link" item to the "My test link" AnyField - Then I add a "Site Tree Link" item to the "My test link" AnyField - And I should see a "Site Tree Link" AnyField modal + @onlyme + Scenario: I can add items to an empty ManyAnyField + And I should see an empty "My test links" ManyAnyField + Then I edit the "My test links" ManyAnyField + And I should see an option to add a "Site Tree Link" item to the "My test links" ManyAnyField + And I should see an option to add a "External Link" item to the "My test links" ManyAnyField + And I should see an option to add a "File Link" item to the "My test links" ManyAnyField + And I should see an option to add a "Email Link" item to the "My test links" ManyAnyField + And I should see an option to add a "Phone Link" item to the "My test links" ManyAnyField + Then I add a "Site Tree Link" item to the "My test links" ManyAnyField + And I should see a "Site Tree Link" ManyAnyField modal Then I select "Contact us" in the "#Form_ModalsAnyFieldForm_PageID_Holder" tree dropdown And I fill in "Title" with "Test link site tree link" + And I select "test-anchor" in the "#Form_ModalsAnyFieldForm_Anchor_Holder" anchor dropdown And I press the "Insert link" button - And I should see a "My test link" AnyField filled with "Test link site tree link" and a description of "Site Tree Link: contact-us" + And I should see a "My test links" ManyAnyField filled with "Test link site tree link" and a description of "Site Tree Link: contact-us" on position 1 Then I press the "Save" button - And I should see a "My test link" AnyField filled with "Test link site tree link" and a description of "Site Tree Link: contact-us" + And I should see a "My test links" ManyAnyField filled with "Test link site tree link" and a description of "Site Tree Link: contact-us" on position 1 Scenario: I can clear a AnyField Then I edit the "My test link" AnyField diff --git a/tests/behat/features/manage-single-link.feature b/tests/behat/features/manage-single-link.feature index e277dca..c628d0f 100644 --- a/tests/behat/features/manage-single-link.feature +++ b/tests/behat/features/manage-single-link.feature @@ -15,6 +15,7 @@ Feature: Manage Single item And I should see an edit page form And I click the "Link test" CMS tab + Scenario: I can fill an empty AnyField with a link And I should see an empty "My test link" AnyField Then I edit the "My test link" AnyField diff --git a/tests/behat/src/AnyFieldContextTrait.php b/tests/behat/src/AnyFieldContextTrait.php new file mode 100644 index 0000000..ee44542 --- /dev/null +++ b/tests/behat/src/AnyFieldContextTrait.php @@ -0,0 +1,180 @@ +getAnyField($label); + Assert::assertNotNull($field, sprintf('HTML field "%s" not found', $label)); + return $field; + } + + /** + * + * @Then /^I should see an empty "(.+?)" AnyField/ + * @param string $not + * @param string $tabLabel + */ + public function AnyFieldShouldBeEmpty(string $label) + { + $field = $this->iShouldSeeAAnyField($label); + $toggle = $field->find('css', '.any-picker-menu__toggle'); + + Assert::assertSame('Add Link', $toggle->getText(), "AnyField field $label is not empty"); + } + + /** + * + * @Then /^I should see a "(.+?)" AnyField filled with "(.+?)" and a description of "(.+?)"/ + * @param string $not + * @param string $tabLabel + */ + public function AnyFieldShouldBeContain(string $label, string $title, string $description) + { + $field = $this->iShouldSeeAAnyField($label); + $titleNode = $field->find('css', '.any-picker-title__title'); + /** @var NodeElement $description */ + $descriptionNode = $field->find('css', '.any-picker-title__type'); + + Assert::assertSame($title, $titleNode->getText(), "$label should contain $title"); + Assert::assertSame($description, $descriptionNode->getText(), "$label should contain $description"); + } + + /** + * + * @Then /^I edit the "(.+?)" AnyField/ + * @param string $not + * @param string $tabLabel + */ + public function EditAnyField(string $label) + { + $field = $this->iShouldSeeAAnyField($label); + $toggle = $field->find('css', 'button.any-picker-menu__toggle, button.any-picker-title'); + + Assert::assertNotNull($toggle); + $toggle->click(); + } + + /** + * + * @Then /^I should see an option to add a "(.+?)" item to the "(.+?)" AnyField/ + * @param string $not + * @param string $tabLabel + */ + public function iShouldSeeAnOptionToAddItem(string $type, string $label) + { + $option = $this->getAnyFieldOption($label, $type); + Assert::assertNotNull($option, "AnyField $type is not there"); + } + + /** + * + * @Then /^I add a "(.+?)" item to the "(.+?)" AnyField/ + * @param string $not + * @param string $tabLabel + */ + public function iAddItemToAnyField(string $type, string $label) + { + $option = $this->getAnyFieldOption($label, $type); + $option->click(); + } + + /** + * @Then /^I should see a clear button in the "(.+?)" AnyField/ + * @param string $title + */ + public function iShouldSeeClearButton(string $title): void + { + $this->getClearButton($title); + } + + /** + * @Then /^I clear the "(.+?)" AnyField/ + * @param string $title + */ + public function iClearAnyField(string $title): void + { + $this->getClearButton($title)->click(); + } + + /** + * Locate an HTML editor field + * + * @param string $locator Raw html field identifier as passed from + */ + protected function getAnyField(string $locator): ?NodeElement + { + $locator = str_replace('\\"', '"', $locator ?? ''); + $page = $this->getMainContext()->getSession()->getPage(); + $input = $page->find('css', 'input[name=\'' . $locator . '\']'); + $fieldId = null; + + if ($input) { + // First lets try to find the hidden input + $fieldId = $input->getAttribute('id'); + } else { + // Then let's try to find the label + $label = $page->findAll('xpath', sprintf('//label[normalize-space()=\'%s\']', $locator)); + if (!empty($label)) { + Assert::assertCount(1, $label, "Found more than one element containing the phrase \"$locator\""); + $label = array_shift($label); + $fieldId = $label->getAttribute('for'); + } + } + + if (empty($fieldId)) { + return null; + } + + $element = $page->find('css', '[data-anyfield-id=\'' . $fieldId . '\']'); + return $element; + } + + protected function getAnyFieldOption(string $locator, string $option): ?NodeElement + { + $field = $this->getAnyField($locator); + Assert::assertNotNull($option, "AnyField field $$locator does not exist"); + + $buttons = $field->findAll('css', '.dropdown-item'); + foreach ($buttons as $button) { + if ($button->getText() === $option) { + return $button; + } + } + + return null; + } + + protected function getClearButton(string $locator): NodeElement + { + $field = $this->getAnyField($locator); + Assert::assertNotNull($field, "AnyField $$locator does not exist"); + + $button = $field->find('css', '.any-picker-title__clear'); + Assert::assertNotNull($button, "Could not find clear button in $locator AnyField"); + + return $button; + } + + /** + * @Then I should see a modal titled :title + * @param string $title + */ + protected function getModal(): ?NodeElement + { + $page = $this->getMainContext()->getSession()->getPage(); + return $page->find('css', '[role=dialog]'); + } + +} diff --git a/tests/behat/src/FixtureContext.php b/tests/behat/src/FixtureContext.php index abd736c..b8cec6e 100644 --- a/tests/behat/src/FixtureContext.php +++ b/tests/behat/src/FixtureContext.php @@ -2,13 +2,11 @@ namespace SilverStripe\AnyField\Tests\Behat\Context; +use SilverStripe\BehatExtension\Context\FixtureContext as BaseFixtureContext; +use PHPUnit\Framework\Assert; use Behat\Mink\Element\DocumentElement; use Behat\Mink\Element\NodeElement; -use PHPUnit\Framework\Assert; -use SilverStripe\BehatExtension\Context\FixtureContext as BaseFixtureContext; use SilverStripe\BehatExtension\Utility\StepHelper; -use SilverStripe\MinkFacebookWebDriver\FacebookWebDriver; -use SilverStripe\Versioned\ChangeSet; /** * Context used to create fixtures in the SilverStripe ORM. @@ -16,181 +14,61 @@ class FixtureContext extends BaseFixtureContext { + use AnyFieldContextTrait; + use ManyAnyFieldContextTrait; + use StepHelper; - public function iShouldSeeAAnyField(string $label) - { - $field = $this->getAnyField($label); - Assert::assertNotNull($field, sprintf('HTML field "%s" not found', $label)); - return $field; - } - - /** - * - * @Then /^I should see an empty "(.+?)" AnyField/ - * @param string $not - * @param string $tabLabel - */ - public function AnyFieldShouldBeEmpty(string $label) - { - $field = $this->iShouldSeeAAnyField($label); - $toggle = $field->find('css', '.any-picker-menu__toggle'); - - Assert::assertSame('Add Link', $toggle->getText(), "AnyField field $label is not empty"); - } - - /** - * - * @Then /^I should see a "(.+?)" AnyField filled with "(.+?)" and a description of "(.+?)"/ - * @param string $not - * @param string $tabLabel - */ - public function AnyFieldShouldBeContain(string $label, string $title, string $description) - { - $field = $this->iShouldSeeAAnyField($label); - $titleNode = $field->find('css', '.any-picker-title__title'); - /** @var NodeElement $description */ - $descriptionNode = $field->find('css', '.any-picker-title__type'); - - Assert::assertSame($title, $titleNode->getText(), "$label should contain $title"); - Assert::assertSame($description, $descriptionNode->getText(), "$label should contain $description"); - } - - /** - * - * @Then /^I edit the "(.+?)" AnyField/ - * @param string $not - * @param string $tabLabel - */ - public function EditAnyField(string $label) - { - $field = $this->iShouldSeeAAnyField($label); - $toggle = $field->find('css', 'button.any-picker-menu__toggle, button.any-picker-title'); - - Assert::assertNotNull($toggle); - $toggle->click(); - } - - /** - * - * @Then /^I should see an option to add a "(.+?)" item to the "(.+?)" AnyField/ - * @param string $not - * @param string $tabLabel - */ - public function iShouldSeeAnOptionToAddItem(string $type, string $label) - { - $option = $this->getAnyFieldOption($label, $type); - Assert::assertNotNull($option, "AnyField $type is not there"); - } /** * - * @Then /^I add a "(.+?)" item to the "(.+?)" AnyField/ + * @Then /^I should see a "(.+?)" (Many)?AnyField modal/ * @param string $not * @param string $tabLabel */ - public function iAddItemToAnyField(string $type, string $label) - { - $option = $this->getAnyFieldOption($label, $type); - $option->click(); - } - - /** - * - * @Then /^I should see a "(.+?)" AnyField modal/ - * @param string $not - * @param string $tabLabel - */ - public function iShouldSeeModal(string $type) + public function iShouldSeeModal(string $type, string $many='') { $modal = $this->getModal(); $title = $modal->find('css', '.modal-title'); - Assert::assertSame($type, $title->getText(), "AnyField modal is not there"); - } - - /** - * @Then /^I should see a clear button in the "(.+?)" AnyField/ - * @param string $title - */ - public function iShouldSeeClearButton(string $title): void - { - $this->getClearButton($title); + Assert::assertSame($type, $title->getText(), "{$many}AnyField modal is not there"); } /** - * @Then /^I clear the "(.+?)" AnyField/ + * @Then I should see a modal titled :title * @param string $title */ - public function iClearAnyField(string $title): void + protected function getModal(): ?NodeElement { - $this->getClearButton($title)->click(); + $page = $this->getMainContext()->getSession()->getPage(); + return $page->find('css', '[role=dialog]'); } /** - * Locate an HTML editor field + * Select a value in the anchor selector field * - * @param string $locator Raw html field identifier as passed from + * @When /^I select "([^"]*)" in the "([^"]*)" anchor dropdown$/ */ - protected function getAnyField(string $locator): ?NodeElement + public function iSelectValueInAnchorDropdown($text, $selector) { - $locator = str_replace('\\"', '"', $locator ?? ''); $page = $this->getMainContext()->getSession()->getPage(); - $input = $page->find('css', 'input[name=\'' . $locator . '\']'); - $fieldId = null; - - if ($input) { - // First lets try to find the hidden input - $fieldId = $input->getAttribute('id'); - } else { - // Then let's try to find the label - $label = $page->findAll('xpath', sprintf('//label[normalize-space()=\'%s\']', $locator)); - if (!empty($label)) { - Assert::assertCount(1, $label, "Found more than one element containing the phrase \"$locator\""); - $label = array_shift($label); - $fieldId = $label->getAttribute('for'); - } - } - - if (empty($fieldId)) { - return null; - } - - $element = $page->find('css', '.any-field-box[data-anyfield-id=\'' . $fieldId . '\']'); - return $element; - } - - protected function getAnyFieldOption(string $locator, string $option): ?NodeElement - { - $field = $this->getAnyField($locator); - Assert::assertNotNull($option, "AnyField field $$locator does not exist"); - - $buttons = $field->findAll('css', '.dropdown-item'); - foreach ($buttons as $button) { - if ($button->getText() === $option) { - return $button; - } - } - - return null; - } - - protected function getClearButton(string $locator): NodeElement - { - $field = $this->getAnyField($locator); - Assert::assertNotNull($field, "AnyField $$locator does not exist"); - - $button = $field->find('css', '.any-picker-title__clear'); - Assert::assertNotNull($button, "Could not find clear button in $locator AnyField"); - - return $button; + /** @var NodeElement $parentElement */ + $parentElement = null; + $this->retryThrowable(function () use (&$parentElement, &$page, $selector) { + $parentElement = $page->find('css', $selector); + Assert::assertNotNull($parentElement, sprintf('"%s" element not found', $selector)); + $page = $this->getMainContext()->getSession()->getPage(); + }); + + $this->retryThrowable(function () use ($parentElement, $selector) { + $dropdown = $parentElement->find('css', '.anchorselectorfield__dropdown-indicator'); + Assert::assertNotNull($dropdown, sprintf('Unable to find the dropdown in "%s"', $selector)); + $dropdown->click(); + }); + + $this->retryThrowable(function () use ($text, $parentElement, $selector) { + $element = $parentElement->find('xpath', sprintf('//*[count(*)=0 and .="%s"]', $text)); + Assert::assertNotNull($element, sprintf('"%s" not found in "%s"', $text, $selector)); + $element->click(); + }); } - /** - * @Then I should see a modal titled :title - * @param string $title - */ - protected function getModal(): ?NodeElement - { - $page = $this->getMainContext()->getSession()->getPage(); - return $page->find('css', '[role=dialog]'); - } } diff --git a/tests/behat/src/ManyAnyFieldContextTrait.php b/tests/behat/src/ManyAnyFieldContextTrait.php new file mode 100644 index 0000000..f834268 --- /dev/null +++ b/tests/behat/src/ManyAnyFieldContextTrait.php @@ -0,0 +1,180 @@ +getManyAnyField($label); + Assert::assertNotNull($field, sprintf('HTML field "%s" not found', $label)); + return $field; + } + + /** + * + * @Then /^I should see an empty "(.+?)" ManyAnyField/ + * @param string $not + * @param string $tabLabel + */ + public function ManyAnyFieldShouldBeEmpty(string $label) + { + $field = $this->iShouldSeeManyAnyField($label); + $items = $this->getManyAnyChildItems($field); + + Assert::assertEmpty($items, "ManyAnyField field $label is not empty"); + } + + /** + * @Then /^I should see a "(.+?)" ManyAnyField filled with "(.+?)" and a description of "(.+?)" on position ([0-9]+)/ + * @param string $not + * @param string $tabLabel + */ + public function ManyAnyFieldShouldBeContain(string $label, string $title, string $description, int $pos) + { + $field = $this->iShouldSeeManyAnyField($label); + $items = $this->getManyAnyChildItems($field); + $item = $items[$pos - 1]; + + $titleNode = $item->find('css', '.any-picker-title__title'); + /** @var NodeElement $description */ + $descriptionNode = $item->find('css', '.any-picker-title__type'); + + Assert::assertSame($title, $titleNode->getText(), "$label should contain $title"); + Assert::assertSame($description, $descriptionNode->getText(), "$label should contain $description"); + } + + /** + * + * @Then /^I edit the "(.+?)" ManyAnyField/ + * @param string $not + * @param string $tabLabel + */ + public function EditManyAnyField(string $label) + { + $field = $this->iShouldSeeManyAnyField($label); + $toggle = $field->find('css', 'button.any-picker-menu__toggle'); + + Assert::assertNotNull($toggle); + $toggle->click(); + } + + /** + * + * @Then /^I should see an option to add a "(.+?)" item to the "(.+?)" ManyAnyField/ + * @param string $not + * @param string $tabLabel + */ + public function iShouldSeeAnOptionToAddItemToManyAnyField(string $type, string $label) + { + $option = $this->getManyAnyFieldOption($label, $type); + Assert::assertNotNull($option, "ManyAnyField $type is not there"); + } + + /** + * + * @Then /^I add a "(.+?)" item to the "(.+?)" ManyAnyField/ + * @param string $not + * @param string $tabLabel + */ + public function iAddItemToManyAnyField(string $type, string $label) + { + $option = $this->getManyAnyFieldOption($label, $type); + $option->click(); + } + + /** + * @Then /^I should see a clear button in the "(.+?)" ManyAnyField/ + * @param string $title + */ + public function iShouldSeeClearButtonInManyAnyField(string $title): void + { + $this->getClearButton($title); + } + + /** + * @Then /^I clear the "(.+?)" ManyAnyField/ + * @param string $title + */ + public function iClearManyAnyField(string $title): void + { + $this->getManyAnyFieldClearButton($title)->click(); + } + + /** + * Locate an HTML editor field + * + * @param string $locator Raw html field identifier as passed from + */ + protected function getManyAnyField(string $locator): ?NodeElement + { + $locator = str_replace('\\"', '"', $locator ?? ''); + $page = $this->getMainContext()->getSession()->getPage(); + $input = $page->find('css', 'input[name=\'' . $locator . '\']'); + $fieldId = null; + + if ($input) { + // First lets try to find the hidden input + $fieldId = $input->getAttribute('id'); + } else { + // Then let's try to find the label + $label = $page->findAll('xpath', sprintf('//label[normalize-space()=\'%s\']', $locator)); + if (!empty($label)) { + Assert::assertCount(1, $label, "Found more than one element containing the phrase \"$locator\""); + $label = array_shift($label); + $fieldId = $label->getAttribute('for'); + } + } + + if (empty($fieldId)) { + return null; + } + + $element = $page->find('css', '[data-manyanyfield-id=\'' . $fieldId . '\']'); + return $element; + } + + /** + * @return NodeElement[] + */ + protected function getManyAnyChildItems(NodeElement $field): array + { + return $field->findAll('css', '.multi-any-picker__list .any-picker-title'); + } + + protected function getManyAnyFieldOption(string $locator, string $option): ?NodeElement + { + $field = $this->getManyAnyField($locator); + Assert::assertNotNull($option, "ManyAnyField field $$locator does not exist"); + + $buttons = $field->findAll('css', '.dropdown-item'); + foreach ($buttons as $button) { + if ($button->getText() === $option) { + return $button; + } + } + + return null; + } + + protected function getManyAnyClearButton(string $locator): NodeElement + { + $field = $this->getAnyField($locator); + Assert::assertNotNull($field, "AnyField $$locator does not exist"); + + $button = $field->find('css', '.any-picker-title__clear'); + Assert::assertNotNull($button, "Could not find clear button in $locator AnyField"); + + return $button; + } + +}